|  | // 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()); | 
|  | } | 
|  | } | 
|  | } |