blob: f2a7e56f0ba3184440a663fe21654e502cb762b2 [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.analysis;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.AspectDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* A dependency of a configured target through a label.
*
* <p>All instances have an explicit configuration, which includes the target and the configuration
* of the dependency configured target and any aspects that may be required, as well as the
* configurations for these aspects and transition keys. {@link Dependency#getTransitionKeys}
* provides some more context on transition keys.
*
* <p>Note that the presence of an aspect here does not necessarily mean that it will be created.
* They will be filtered based on the {@link TransitiveInfoProvider} instances their associated
* configured targets have (we cannot do that here because the configured targets are not available
* yet). No error or warning is reported in this case, because it is expected that rules sometimes
* over-approximate the providers they supply in their definitions.
*/
public abstract class Dependency {
/** Builder to assist in creating dependency instances. */
public static class Builder {
private Label label;
private BuildConfiguration configuration;
private AspectCollection aspects = AspectCollection.EMPTY;
private List<String> transitionKeys = new ArrayList<>();
public Builder setLabel(Label label) {
this.label = Preconditions.checkNotNull(label);
return this;
}
public Builder setConfiguration(BuildConfiguration configuration) {
this.configuration = configuration;
return this;
}
/** Explicitly set the configuration for this dependency to null. */
public Builder withNullConfiguration() {
return setConfiguration(null);
}
/** Add aspects to this Dependency. The same configuration is applied to all aspects. */
public Builder setAspects(AspectCollection aspects) {
this.aspects = aspects;
return this;
}
public Builder addTransitionKey(String key) {
this.transitionKeys.add(key);
return this;
}
public Builder addTransitionKeys(Collection<String> keys) {
this.transitionKeys = new ArrayList<>(keys);
return this;
}
/** Returns the full Dependency instance. */
public Dependency build() {
if (configuration == null) {
Preconditions.checkState(aspects.equals(AspectCollection.EMPTY));
return new NullConfigurationDependency(label, ImmutableList.copyOf(transitionKeys));
}
// Use the target configuration for all aspects with none of their own.
Map<AspectDescriptor, BuildConfiguration> aspectConfigurations = new HashMap<>();
for (AspectDescriptor aspect : aspects.getAllAspects()) {
aspectConfigurations.put(aspect, configuration);
}
return new ExplicitConfigurationDependency(
label,
configuration,
aspects,
ImmutableMap.copyOf(aspectConfigurations),
ImmutableList.copyOf(transitionKeys));
}
}
/** Returns a new {@link Builder} to create {@link Dependency} instances. */
public static Builder builder() {
return new Builder();
}
protected final Label label;
/**
* Only the implementations below should extend this class. Use the factory methods above to
* create new Dependencies.
*/
private Dependency(Label label) {
this.label = Preconditions.checkNotNull(label);
}
/** Returns the label of the target this dependency points to. */
public Label getLabel() {
return label;
}
/**
* Returns the explicit configuration intended for this dependency.
*/
@Nullable
public abstract BuildConfiguration getConfiguration();
/**
* Returns the set of aspects which should be evaluated and combined with the configured target
* pointed to by this dependency.
*
* @see #getAspectConfiguration(AspectDescriptor)
*/
public abstract AspectCollection getAspects();
/** Returns the configuration an aspect should be evaluated with. */
public abstract BuildConfiguration getAspectConfiguration(AspectDescriptor aspect);
/**
* Returns the keys of a configuration transition, if exist, associated with this dependency. See
* {@link ConfigurationTransition#apply} for more details. Normally, this returns an empty list,
* when there was no configuration transition in effect, or one with a single entry, when there
* was a specific configuration transition result that led to this. It may also return a list with
* multiple entries if the dependency has a null configuration, yet the outgoing edge has a split
* transition. In such cases all transition keys returned by the transition are tagged to the
* dependency.
*/
public abstract ImmutableList<String> getTransitionKeys();
/**
* Implementation of a dependency with no configuration, suitable for, e.g., source files or
* visibility.
*/
private static final class NullConfigurationDependency extends Dependency {
private final ImmutableList<String> transitionKeys;
public NullConfigurationDependency(Label label, ImmutableList<String> transitionKeys) {
super(label);
this.transitionKeys = Preconditions.checkNotNull(transitionKeys);
}
@Nullable
@Override
public BuildConfiguration getConfiguration() {
return null;
}
@Override
public AspectCollection getAspects() {
return AspectCollection.EMPTY;
}
@Override
public BuildConfiguration getAspectConfiguration(AspectDescriptor aspect) {
return null;
}
@Override
public ImmutableList<String> getTransitionKeys() {
return transitionKeys;
}
@Override
public int hashCode() {
return label.hashCode();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof NullConfigurationDependency)) {
return false;
}
NullConfigurationDependency otherDep = (NullConfigurationDependency) other;
return label.equals(otherDep.label);
}
@Override
public String toString() {
return String.format("NullConfigurationDependency{label=%s}", label);
}
}
/**
* Implementation of a dependency with an explicitly set configuration.
*/
private static final class ExplicitConfigurationDependency extends Dependency {
private final BuildConfiguration configuration;
private final AspectCollection aspects;
private final ImmutableMap<AspectDescriptor, BuildConfiguration> aspectConfigurations;
private final ImmutableList<String> transitionKeys;
public ExplicitConfigurationDependency(
Label label,
BuildConfiguration configuration,
AspectCollection aspects,
ImmutableMap<AspectDescriptor, BuildConfiguration> aspectConfigurations,
ImmutableList<String> transitionKeys) {
super(label);
this.configuration = Preconditions.checkNotNull(configuration);
this.aspects = Preconditions.checkNotNull(aspects);
this.aspectConfigurations = Preconditions.checkNotNull(aspectConfigurations);
this.transitionKeys = Preconditions.checkNotNull(transitionKeys);
}
@Override
public BuildConfiguration getConfiguration() {
return configuration;
}
@Override
public AspectCollection getAspects() {
return aspects;
}
@Override
public BuildConfiguration getAspectConfiguration(AspectDescriptor aspect) {
return aspectConfigurations.get(aspect);
}
@Override
public ImmutableList<String> getTransitionKeys() {
return transitionKeys;
}
@Override
public int hashCode() {
return Objects.hash(label, configuration, aspectConfigurations, transitionKeys);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ExplicitConfigurationDependency)) {
return false;
}
ExplicitConfigurationDependency otherDep = (ExplicitConfigurationDependency) other;
return label.equals(otherDep.label)
&& configuration.equals(otherDep.configuration)
&& aspects.equals(otherDep.aspects)
&& aspectConfigurations.equals(otherDep.aspectConfigurations);
}
@Override
public String toString() {
return String.format(
"%s{label=%s, configuration=%s, aspectConfigurations=%s}",
getClass().getSimpleName(), label, configuration, aspectConfigurations);
}
}
}