// 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.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.util.Preconditions;
/**
* Represents a constraint on a set of providers required by a dependency (of a rule
* or an aspect).
*
* Currently we support three kinds of constraints:
*
* - accept any dependency.
* - accept no dependency (used for aspects-on-aspects to indicate
* that an aspect never wants to see any other aspect applied to a target.
* - 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.
*
*
*/
@Immutable
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>> nativeProviders;
/**
* Sets of native providers.
* If non-empty, {@link #constraint} is {@link Constraint#RESTRICTED}
*/
private final ImmutableList> skylarkProviders;
/**
* Represents one of the constraints as desctibed in {@link RequiredProviders}
*/
private enum Constraint {
/** Accept any dependency */
ANY {
@Override
public boolean satisfies(AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders) {
return true;
}
@Override
public boolean satisfies(Predicate> hasNativeProvider,
Predicate hasSkylarkProvider,
RequiredProviders requiredProviders) {
return true;
}
},
/** Accept no dependency */
NONE {
@Override
public boolean satisfies(AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders) {
return false;
}
@Override
public boolean satisfies(Predicate> hasNativeProvider,
Predicate hasSkylarkProvider,
RequiredProviders requiredProviders) {
return false;
}
},
/** Accept a dependency that has all providers from one of the sets. */
RESTRICTED {
@Override
public boolean satisfies(Predicate> hasNativeProvider,
Predicate hasSkylarkProvider,
RequiredProviders requiredProviders) {
for (ImmutableSet> nativeProviderSet : requiredProviders.nativeProviders) {
if (Iterables.all(nativeProviderSet, hasNativeProvider)) {
return true;
}
}
for (ImmutableSet skylarkProviderSet
: requiredProviders.skylarkProviders) {
if (Iterables.all(skylarkProviderSet, hasSkylarkProvider)) {
return true;
}
}
return false;
}
};
/** Checks if {@code advertisedProviderSet} satisfies these {@code RequiredProviders} */
public boolean satisfies(final AdvertisedProviderSet advertisedProviderSet,
RequiredProviders requiredProviders) {
if (advertisedProviderSet.canHaveAnyProvider()) {
return true;
}
return satisfies(
new Predicate>() {
@Override
public boolean apply(Class> aClass) {
return advertisedProviderSet.getNativeProviders().contains(aClass);
}
},
Predicates.in(advertisedProviderSet.getSkylarkProviders()),
requiredProviders
);
}
/**
* Checks if a set of providers encoded by predicates {@code hasNativeProviders}
* and {@code hasSkylarkProvider} satisfies these {@code RequiredProviders}
*/
abstract boolean satisfies(Predicate> hasNativeProvider,
Predicate hasSkylarkProvider,
RequiredProviders requiredProviders);
}
/** Checks if {@code advertisedProviderSet} satisfies this {@code RequiredProviders} instance. */
public boolean isSatisfiedBy(AdvertisedProviderSet advertisedProviderSet) {
return constraint.satisfies(advertisedProviderSet, this);
}
/**
* Checks if a set of providers encoded by predicates {@code hasNativeProviders}
* and {@code hasSkylarkProvider} satisfies this {@code RequiredProviders} instance.
*/
public boolean isSatisfiedBy(
Predicate> hasNativeProvider,
Predicate hasSkylarkProvider) {
return constraint.satisfies(hasNativeProvider, hasSkylarkProvider, this);
}
private RequiredProviders(
Constraint constraint,
ImmutableList>> nativeProviders,
ImmutableList> skylarkProviders) {
this.constraint = constraint;
Preconditions.checkState(constraint.equals(Constraint.RESTRICTED)
|| (nativeProviders.isEmpty() && skylarkProviders.isEmpty())
);
this.nativeProviders = nativeProviders;
this.skylarkProviders = 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);
}
/** A builder for {@link RequiredProviders} */
public static class Builder {
private final ImmutableList.Builder>> nativeProviders;
private final ImmutableList.Builder> 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 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> 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());
}
}
}