|  | // 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.Preconditions; | 
|  | import com.google.common.collect.ImmutableSet; | 
|  | import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | 
|  | import com.google.devtools.build.lib.util.Fingerprint; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Objects; | 
|  |  | 
|  | /** | 
|  | * Captures the set of providers rules and aspects can advertise. It is either of: | 
|  | * | 
|  | * <ul> | 
|  | *   <li>a set of builtin and Starlark providers | 
|  | *   <li>"can have any provider" set that alias rules have. | 
|  | * </ul> | 
|  | * | 
|  | * <p>Built-in providers should in theory only contain subclasses of {@link | 
|  | * com.google.devtools.build.lib.analysis.TransitiveInfoProvider}, but our current dependency | 
|  | * structure does not allow a reference to that class here. | 
|  | */ | 
|  | @Immutable | 
|  | public final class AdvertisedProviderSet { | 
|  | private final boolean canHaveAnyProvider; | 
|  | private final ImmutableSet<Class<?>> builtinProviders; | 
|  | private final ImmutableSet<StarlarkProviderIdentifier> starlarkProviders; | 
|  |  | 
|  | private AdvertisedProviderSet( | 
|  | boolean canHaveAnyProvider, | 
|  | ImmutableSet<Class<?>> builtinProviders, | 
|  | ImmutableSet<StarlarkProviderIdentifier> starlarkProviders) { | 
|  | this.canHaveAnyProvider = canHaveAnyProvider; | 
|  | this.builtinProviders = builtinProviders; | 
|  | this.starlarkProviders = starlarkProviders; | 
|  | } | 
|  |  | 
|  | public static final AdvertisedProviderSet ANY = | 
|  | new AdvertisedProviderSet(true, ImmutableSet.of(), ImmutableSet.of()); | 
|  | public static final AdvertisedProviderSet EMPTY = | 
|  | new AdvertisedProviderSet(false, ImmutableSet.of(), ImmutableSet.of()); | 
|  |  | 
|  | public static AdvertisedProviderSet create( | 
|  | ImmutableSet<Class<?>> builtinProviders, | 
|  | ImmutableSet<StarlarkProviderIdentifier> starlarkProviders) { | 
|  | if (builtinProviders.isEmpty() && starlarkProviders.isEmpty()) { | 
|  | return EMPTY; | 
|  | } | 
|  | return new AdvertisedProviderSet(false, builtinProviders, starlarkProviders); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return Objects.hash(canHaveAnyProvider, builtinProviders, starlarkProviders); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object obj) { | 
|  | if (this == obj) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!(obj instanceof AdvertisedProviderSet)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | AdvertisedProviderSet that = (AdvertisedProviderSet) obj; | 
|  | return this.canHaveAnyProvider == that.canHaveAnyProvider | 
|  | && Objects.equals(this.builtinProviders, that.builtinProviders) | 
|  | && Objects.equals(this.starlarkProviders, that.starlarkProviders); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | if (canHaveAnyProvider()) { | 
|  | return "Any Provider"; | 
|  | } | 
|  | return String.format( | 
|  | "allowed built-in providers=%s, allowed Starlark providers=%s", | 
|  | builtinProviders, starlarkProviders); | 
|  | } | 
|  |  | 
|  | /** Checks whether the rule can have any provider. | 
|  | * | 
|  | *  Used for alias rules. | 
|  | */ | 
|  | public boolean canHaveAnyProvider() { | 
|  | return canHaveAnyProvider; | 
|  | } | 
|  |  | 
|  | /** Get all advertised built-in providers. */ | 
|  | public ImmutableSet<Class<?>> getBuiltinProviders() { | 
|  | return builtinProviders; | 
|  | } | 
|  |  | 
|  | /** Get all advertised Starlark providers. */ | 
|  | public ImmutableSet<StarlarkProviderIdentifier> getStarlarkProviders() { | 
|  | return starlarkProviders; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Adds the fingerprints of this {@link AdvertisedProviderSet} into {@code fp}. | 
|  | * | 
|  | * <p>Fingerprints of {@link AdvertisedProviderSet} must have the following properties: | 
|  | * | 
|  | * <ul> | 
|  | *   <li>If {@code aps1.equals(aps2)} then {@code aps1} and {@code aps2} have the same | 
|  | *       fingerprint. | 
|  | *   <li>If {@code !aps1.equals(aps2)} then {@code aps1} and {@code aps2} don't have the same | 
|  | *       fingerprint (except for unintentional digest collisions). | 
|  | * </ul> | 
|  | * | 
|  | * <p>In other words, this method is a proxy for {@link #equals}. These properties *do not* need | 
|  | * to be maintained across Blaze versions (e.g. there's no need to worry about historical | 
|  | * serialized fingerprints). | 
|  | */ | 
|  | public void fingerprint(Fingerprint fp) { | 
|  | fp.addBoolean(canHaveAnyProvider); | 
|  | // #builtinProviders and #starlarkProviders are ordered according to the calls to the builder | 
|  | // methods, and that order is assumed to be deterministic. | 
|  | builtinProviders.forEach(clazz -> fp.addString(clazz.getCanonicalName())); | 
|  | starlarkProviders.forEach(starlarkProvider -> starlarkProvider.fingerprint(fp)); | 
|  | } | 
|  |  | 
|  | public static Builder builder() { | 
|  | return new Builder(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns {@code true} if this provider set can have any provider, or if it advertises the | 
|  | * specific Starlark provider requested. | 
|  | */ | 
|  | public boolean advertises(StarlarkProviderIdentifier starlarkProvider) { | 
|  | if (canHaveAnyProvider()) { | 
|  | return true; | 
|  | } | 
|  | return starlarkProviders.contains(starlarkProvider); | 
|  | } | 
|  |  | 
|  | /** Builder for {@link AdvertisedProviderSet} */ | 
|  | public static class Builder { | 
|  | private boolean canHaveAnyProvider; | 
|  | private final ArrayList<Class<?>> builtinProviders; | 
|  | private final ArrayList<StarlarkProviderIdentifier> starlarkProviders; | 
|  |  | 
|  | private Builder() { | 
|  | builtinProviders = new ArrayList<>(); | 
|  | starlarkProviders = new ArrayList<>(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Advertise all providers inherited from a parent rule. | 
|  | */ | 
|  | public Builder addParent(AdvertisedProviderSet parentSet) { | 
|  | Preconditions.checkState(!canHaveAnyProvider, "Alias rules inherit from no other rules"); | 
|  | Preconditions.checkState(!parentSet.canHaveAnyProvider(), | 
|  | "Cannot inherit from alias rules"); | 
|  | builtinProviders.addAll(parentSet.getBuiltinProviders()); | 
|  | starlarkProviders.addAll(parentSet.getStarlarkProviders()); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder addBuiltin(Class<?> builtinProvider) { | 
|  | this.builtinProviders.add(builtinProvider); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public void canHaveAnyProvider() { | 
|  | Preconditions.checkState(builtinProviders.isEmpty() && starlarkProviders.isEmpty()); | 
|  | this.canHaveAnyProvider = true; | 
|  | } | 
|  |  | 
|  | public AdvertisedProviderSet build() { | 
|  | if (canHaveAnyProvider) { | 
|  | Preconditions.checkState(builtinProviders.isEmpty() && starlarkProviders.isEmpty()); | 
|  | return ANY; | 
|  | } | 
|  | return AdvertisedProviderSet.create( | 
|  | ImmutableSet.copyOf(builtinProviders), ImmutableSet.copyOf(starlarkProviders)); | 
|  | } | 
|  |  | 
|  | public Builder addStarlark(String providerName) { | 
|  | starlarkProviders.add(StarlarkProviderIdentifier.forLegacy(providerName)); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Builder addStarlark(StarlarkProviderIdentifier id) { | 
|  | starlarkProviders.add(id); | 
|  | return this; | 
|  | } | 
|  | } | 
|  | } |