blob: 2f026c84a214fb648f2f049d230a6204131bec4f [file] [log] [blame]
// Copyright 2016 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.packages;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.annotation.Nullable;
/**
* Represents a constraint on a set of providers required by a dependency (of a rule or an aspect).
*
* <p>Currently we support three kinds of constraints:
*
* <ul>
* <li>accept any dependency.
* <li>accept no dependency (used for aspects-on-aspects to indicate that an aspect never wants to
* see any other aspect applied to a target.
* <li>accept a dependency that provides all providers from one of several sets of providers. It
* just so happens that in all current usages these sets are either all native providers or
* all Skylark providers, so this is the only use case this class currently supports.
* </ul>
*/
@Immutable
@AutoCodec
public final class RequiredProviders {
/** A constraint: either ANY, NONE, or RESTRICTED */
private final Constraint constraint;
/**
* Sets of native providers.
* If non-empty, {@link #constraint} is {@link Constraint#RESTRICTED}
*/
private final ImmutableList<ImmutableSet<Class<?>>> nativeProviders;
/**
* Sets of native providers.
* If non-empty, {@link #constraint} is {@link Constraint#RESTRICTED}
*/
private final ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> skylarkProviders;
public String getDescription() {
return constraint.getDescription(this);
}
@Override
public String toString() {
return getDescription();
}
/** Represents one of the constraints as desctibed in {@link RequiredProviders} */
@VisibleForSerialization
enum Constraint {
/** Accept any dependency */
ANY {
@Override
public boolean satisfies(
AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders,
Builder missing) {
return true;
}
@Override
public boolean satisfies(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider,
RequiredProviders requiredProviders,
Builder missingProviders) {
return true;
}
@Override
Builder copyAsBuilder(RequiredProviders providers) {
return acceptAnyBuilder();
}
@Override
public String getDescription(RequiredProviders providers) {
return "no providers required";
}
},
/** Accept no dependency */
NONE {
@Override
public boolean satisfies(
AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders,
Builder missing) {
return false;
}
@Override
public boolean satisfies(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider,
RequiredProviders requiredProviders,
Builder missingProviders) {
return false;
}
@Override
Builder copyAsBuilder(RequiredProviders providers) {
return acceptNoneBuilder();
}
@Override
public String getDescription(RequiredProviders providers) {
return "no providers accepted";
}
},
/** Accept a dependency that has all providers from one of the sets. */
RESTRICTED {
@Override
public boolean satisfies(
final AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders,
Builder missing) {
if (advertisedProviderSet.canHaveAnyProvider()) {
return true;
}
return satisfies(
advertisedProviderSet.getNativeProviders()::contains,
advertisedProviderSet.getSkylarkProviders()::contains,
requiredProviders,
missing);
}
@Override
public boolean satisfies(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider,
RequiredProviders requiredProviders,
Builder missingProviders) {
for (ImmutableSet<Class<?>> nativeProviderSet : requiredProviders.nativeProviders) {
if (nativeProviderSet.stream().allMatch(hasNativeProvider)) {
return true;
}
// Collect missing providers
if (missingProviders != null) {
missingProviders.addNativeSet(
nativeProviderSet
.stream()
.filter(hasNativeProvider.negate())
.collect(ImmutableSet.toImmutableSet()));
}
}
for (ImmutableSet<SkylarkProviderIdentifier> skylarkProviderSet
: requiredProviders.skylarkProviders) {
if (skylarkProviderSet.stream().allMatch(hasSkylarkProvider)) {
return true;
}
// Collect missing providers
if (missingProviders != null) {
missingProviders.addSkylarkSet(
skylarkProviderSet
.stream()
.filter(hasSkylarkProvider.negate())
.collect(ImmutableSet.toImmutableSet()));
}
}
return false;
}
@Override
Builder copyAsBuilder(RequiredProviders providers) {
Builder result = acceptAnyBuilder();
for (ImmutableSet<Class<?>> nativeProviderSet : providers.nativeProviders) {
result.addNativeSet(nativeProviderSet);
}
for (ImmutableSet<SkylarkProviderIdentifier> skylarkProviderSet :
providers.skylarkProviders) {
result.addSkylarkSet(skylarkProviderSet);
}
return result;
}
@Override
public String getDescription(RequiredProviders providers) {
StringBuilder result = new StringBuilder();
describe(result, providers.nativeProviders, Class::getSimpleName);
describe(result, providers.skylarkProviders, id -> "'" + id.toString() + "'");
return result.toString();
}
};
/** Checks if {@code advertisedProviderSet} satisfies these {@code RequiredProviders} */
public abstract boolean satisfies(
AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders,
Builder missing);
/**
* Checks if a set of providers encoded by predicates {@code hasNativeProviders} and {@code
* hasSkylarkProvider} satisfies these {@code RequiredProviders}
*/
abstract boolean satisfies(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider,
RequiredProviders requiredProviders,
@Nullable Builder missingProviders);
abstract Builder copyAsBuilder(RequiredProviders providers);
/** Returns a string describing the providers that can be presented to the user. */
abstract String getDescription(RequiredProviders providers);
}
/** Checks if {@code advertisedProviderSet} satisfies this {@code RequiredProviders} instance. */
public boolean isSatisfiedBy(AdvertisedProviderSet advertisedProviderSet) {
return constraint.satisfies(advertisedProviderSet, this, null);
}
/**
* Checks if a set of providers encoded by predicates {@code hasNativeProviders}
* and {@code hasSkylarkProvider} satisfies this {@code RequiredProviders} instance.
*/
public boolean isSatisfiedBy(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider) {
return constraint.satisfies(hasNativeProvider, hasSkylarkProvider, this, null);
}
/**
* Returns providers that are missing. If none are missing, returns {@code RequiredProviders} that
* accept anything.
*/
public RequiredProviders getMissing(
Predicate<Class<?>> hasNativeProvider,
Predicate<SkylarkProviderIdentifier> hasSkylarkProvider) {
Builder builder = acceptAnyBuilder();
if (constraint.satisfies(hasNativeProvider, hasSkylarkProvider, this, builder)) {
// Ignore all collected missing providers.
return acceptAnyBuilder().build();
}
return builder.build();
}
/**
* Returns providers that are missing. If none are missing, returns {@code RequiredProviders} that
* accept anything.
*/
public RequiredProviders getMissing(AdvertisedProviderSet set) {
Builder builder = acceptAnyBuilder();
if (constraint.satisfies(set, this, builder)) {
// Ignore all collected missing providers.
return acceptAnyBuilder().build();
}
return builder.build();
}
/** Returns true if this {@code RequiredProviders} instance accept any set of providers. */
public boolean acceptsAny() {
return constraint.equals(Constraint.ANY);
}
@VisibleForSerialization
RequiredProviders(
Constraint constraint,
ImmutableList<ImmutableSet<Class<?>>> nativeProviders,
ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> skylarkProviders) {
this.constraint = constraint;
Preconditions.checkState(constraint.equals(Constraint.RESTRICTED)
|| (nativeProviders.isEmpty() && skylarkProviders.isEmpty())
);
this.nativeProviders = nativeProviders;
this.skylarkProviders = skylarkProviders;
}
/** Helper method to describe lists of sets of things. */
private static <T> void describe(
StringBuilder result,
ImmutableList<ImmutableSet<T>> listOfSets,
Function<T, String> describeOne) {
Joiner joiner = Joiner.on(", ");
for (ImmutableSet<T> ids : listOfSets) {
if (result.length() > 0) {
result.append(" or ");
}
result.append((ids.size() > 1) ? "[" : "");
joiner.appendTo(result, ids.stream().map(describeOne).iterator());
result.append((ids.size() > 1) ? "]" : "");
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RequiredProviders that = (RequiredProviders) o;
return constraint == that.constraint
&& Objects.equals(nativeProviders, that.nativeProviders)
&& Objects.equals(skylarkProviders, that.skylarkProviders);
}
@Override
public int hashCode() {
return Objects.hash(constraint, nativeProviders, skylarkProviders);
}
/**
* A builder for {@link RequiredProviders} that accepts any dependency
* unless restriction provider sets are added.
*/
public static Builder acceptAnyBuilder() {
return new Builder(false);
}
/**
* A builder for {@link RequiredProviders} that accepts no dependency
* unless restriction provider sets are added.
*/
public static Builder acceptNoneBuilder() {
return new Builder(true);
}
/** Returns a Builder initialized to the same value as this {@code RequiredProvider} */
public Builder copyAsBuilder() {
return constraint.copyAsBuilder(this);
}
/** A builder for {@link RequiredProviders} */
public static class Builder {
private final ImmutableList.Builder<ImmutableSet<Class<?>>> nativeProviders;
private final ImmutableList.Builder<ImmutableSet<SkylarkProviderIdentifier>> skylarkProviders;
private Constraint constraint;
private Builder(boolean acceptNone) {
constraint = acceptNone ? Constraint.NONE : Constraint.ANY;
nativeProviders = ImmutableList.builder();
skylarkProviders = ImmutableList.builder();
}
/**
* Add an alternative set of Skylark providers.
*
* If all of these providers are present in the dependency, the dependency satisfies
* {@link RequiredProviders}.
*/
public Builder addSkylarkSet(ImmutableSet<SkylarkProviderIdentifier> skylarkProviderSet) {
constraint = Constraint.RESTRICTED;
Preconditions.checkState(!skylarkProviderSet.isEmpty());
this.skylarkProviders.add(skylarkProviderSet);
return this;
}
/**
* Add an alternative set of native providers.
*
* If all of these providers are present in the dependency, the dependency satisfies
* {@link RequiredProviders}.
*/
public Builder addNativeSet(ImmutableSet<Class<?>> nativeProviderSet) {
constraint = Constraint.RESTRICTED;
Preconditions.checkState(!nativeProviderSet.isEmpty());
this.nativeProviders.add(nativeProviderSet);
return this;
}
public RequiredProviders build() {
return new RequiredProviders(constraint, nativeProviders.build(), skylarkProviders.build());
}
}
}