// 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.rules.java; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap; import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.packages.ClassObjectConstructor; import com.google.devtools.build.lib.packages.NativeClassObjectConstructor; import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.annotation.Nullable; /** A Skylark declared provider that encapsulates all providers that are needed by Java rules. */ @Immutable public final class JavaProvider extends SkylarkClassObject implements TransitiveInfoProvider { public static final ClassObjectConstructor JAVA_PROVIDER = new NativeClassObjectConstructor("java_common.provider") { }; private static final ImmutableSet<Class<? extends TransitiveInfoProvider>> ALLOWED_PROVIDERS = ImmutableSet.of( JavaCompilationArgsProvider.class, JavaSourceJarsProvider.class, ProtoJavaApiInfoAspectProvider.class, JavaRuleOutputJarsProvider.class, JavaRunfilesProvider.class ); private final TransitiveInfoProviderMap providers; /** Returns the instance for the provided providerClass, or <tt>null</tt> if not present. */ @Nullable public <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) { return providers.getProvider(providerClass); } public TransitiveInfoProviderMap getProviders() { return providers; } /** * Merges the given providers into one {@link JavaProvider}. All the providers with the same type * in the given list are merged into one provider that is added to the resulting * {@link JavaProvider}. */ public static JavaProvider merge(List<JavaProvider> providers) { List<JavaCompilationArgsProvider> javaCompilationArgsProviders = JavaProvider.fetchProvidersFromList(providers, JavaCompilationArgsProvider.class); List<JavaSourceJarsProvider> javaSourceJarsProviders = JavaProvider.fetchProvidersFromList(providers, JavaSourceJarsProvider.class); List<ProtoJavaApiInfoAspectProvider> protoJavaApiInfoAspectProviders = JavaProvider.fetchProvidersFromList(providers, ProtoJavaApiInfoAspectProvider.class); List<JavaRunfilesProvider> javaRunfilesProviders = JavaProvider.fetchProvidersFromList(providers, JavaRunfilesProvider.class); Runfiles mergedRunfiles = Runfiles.EMPTY; for (JavaRunfilesProvider javaRunfilesProvider : javaRunfilesProviders) { Runfiles runfiles = javaRunfilesProvider.getRunfiles(); mergedRunfiles = mergedRunfiles == Runfiles.EMPTY ? runfiles : mergedRunfiles.merge(runfiles); } return JavaProvider.Builder.create() .addProvider( JavaCompilationArgsProvider.class, JavaCompilationArgsProvider.merge(javaCompilationArgsProviders)) .addProvider( JavaSourceJarsProvider.class, JavaSourceJarsProvider.merge(javaSourceJarsProviders)) .addProvider( ProtoJavaApiInfoAspectProvider.class, ProtoJavaApiInfoAspectProvider.merge(protoJavaApiInfoAspectProviders)) // When a rule merges multiple JavaProviders, its purpose is to pass on information, so // it doesn't have any output jars. .addProvider(JavaRuleOutputJarsProvider.class, JavaRuleOutputJarsProvider.builder().build()) .addProvider(JavaRunfilesProvider.class, new JavaRunfilesProvider(mergedRunfiles)) .build(); } /** * Returns a list of providers of the specified class, fetched from the given list of * {@link JavaProvider}s. * Returns an empty list if no providers can be fetched. * Returns a list of the same size as the given list if the requested providers are of type * JavaCompilationArgsProvider. */ public static <C extends TransitiveInfoProvider> List<C> fetchProvidersFromList( Iterable<JavaProvider> javaProviders, Class<C> providersClass) { List<C> fetchedProviders = new LinkedList<>(); for (JavaProvider javaProvider : javaProviders) { C provider = javaProvider.getProvider(providersClass); if (provider != null) { fetchedProviders.add(provider); } } return fetchedProviders; } /** * Returns a provider of the specified class, fetched from the specified target or, if not found, * from the JavaProvider of the given target. JavaProvider can be found as a declared provider * in SkylarkProviders. * Returns null if no such provider exists. * * <p>A target can either have both the specified provider and JavaProvider that encapsulates the * same information, or just one of them.</p> */ @Nullable public static <T extends TransitiveInfoProvider> T getProvider( Class<T> providerClass, TransitiveInfoCollection target) { T provider = target.getProvider(providerClass); if (provider != null) { return provider; } JavaProvider javaProvider = (JavaProvider) target.get(JavaProvider.JAVA_PROVIDER.getKey()); if (javaProvider == null) { return null; } return javaProvider.getProvider(providerClass); } public static <T extends TransitiveInfoProvider> List<T> getProvidersFromListOfTargets( Class<T> providerClass, Iterable<? extends TransitiveInfoCollection> targets) { List<T> providersList = new ArrayList<>(); for (TransitiveInfoCollection target : targets) { T provider = getProvider(providerClass, target); if (provider != null) { providersList.add(provider); } } return providersList; } private JavaProvider(TransitiveInfoProviderMap providers) { super(JAVA_PROVIDER, ImmutableMap.<String, Object>of( "transitive_runtime_jars", SkylarkNestedSet.of( Artifact.class, providers.getProvider(JavaCompilationArgsProvider.class) .getRecursiveJavaCompilationArgs().getRuntimeJars()), "compile_jars", SkylarkNestedSet.of( Artifact.class, providers.getProvider(JavaCompilationArgsProvider.class) .getJavaCompilationArgs().getCompileTimeJars()) )); this.providers = providers; } /** * A Builder for {@link JavaProvider}. */ public static class Builder { TransitiveInfoProviderMapBuilder providerMap; private Builder(TransitiveInfoProviderMapBuilder providerMap) { this.providerMap = providerMap; } public static Builder create() { return new Builder(new TransitiveInfoProviderMapBuilder()); } public static Builder copyOf(JavaProvider javaProvider) { return new Builder( new TransitiveInfoProviderMapBuilder().addAll(javaProvider.getProviders())); } public <P extends TransitiveInfoProvider> Builder addProvider( Class<P> providerClass, TransitiveInfoProvider provider) { Preconditions.checkArgument(ALLOWED_PROVIDERS.contains(providerClass)); providerMap.put(providerClass, provider); return this; } public JavaProvider build() { Preconditions.checkArgument(providerMap.contains(JavaCompilationArgsProvider.class)); return new JavaProvider(providerMap.build()); } } }