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