// Copyright 2015 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.android; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import java.util.Optional; import javax.annotation.Nullable; /** * Represents a container for the resource dependencies for a given target. This abstraction * simplifies the process of managing and exporting the direct and transitive resource dependencies * of an android rule, as well as providing type safety. * *

The transitive and direct dependencies are not guaranteed to be disjoint. If a library is * included in both the transitive and direct dependencies, it will appear twice. This requires * consumers to manage duplicated resources gracefully. * *

TODO(b/76418178): Once resource processing is fully decoupled from asset and manifest * processing, remove asset and manifest fields from this class. */ @Immutable public final class ResourceDependencies { /** * Contains all the transitive resources that are not generated by the direct ancestors of the * current rule. * * @deprecated We are migrating towards storing each type of Artifact in a different NestedSet. * This should allow greater efficiency since we don't need to unroll this NestedSet to get a * particular input. TODO (b/67996945): Complete this migration. */ @Deprecated private final NestedSet transitiveResourceContainers; /** * Contains all the direct dependencies of the current target. Since a given direct dependency can * act as a "forwarding" library, collecting all the direct resource from it's dependencies and * providing them as "direct" dependencies to maintain merge order, this uses a NestedSet to * properly maintain ordering and ease of merging. * * @deprecated Similarly to {@link #transitiveResourceContainers}, we are moving away from storing * ResourceContainer objects here. TODO (b/67996945): Complete this migration. */ @Deprecated private final NestedSet directResourceContainers; /** * Transitive resource files for this target. * *

We keep them separate from the {@code transitiveAssets} so that we can filter them. */ private final NestedSet transitiveResources; private final NestedSet transitiveManifests; private final NestedSet transitiveAapt2RTxt; private final NestedSet transitiveSymbolsBin; private final NestedSet transitiveCompiledSymbols; private final NestedSet transitiveStaticLib; private final NestedSet transitiveRTxt; /** Whether the resources of the current rule should be treated as neverlink. */ private final boolean neverlink; public static ResourceDependencies fromRuleDeps(RuleContext ruleContext, boolean neverlink) { return fromProviders( AndroidCommon.getTransitivePrerequisites( ruleContext, Mode.TARGET, AndroidResourcesInfo.PROVIDER), neverlink); } public static ResourceDependencies fromProviders( Iterable providers, boolean neverlink) { NestedSetBuilder transitiveDependencies = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder directDependencies = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveResources = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveManifests = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveAapt2RTxt = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveSymbolsBin = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveCompiledSymbols = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveStaticLib = NestedSetBuilder.naiveLinkOrder(); NestedSetBuilder transitiveRTxt = NestedSetBuilder.naiveLinkOrder(); for (AndroidResourcesInfo resources : providers) { transitiveDependencies.addTransitive(resources.getTransitiveAndroidResources()); directDependencies.addTransitive(resources.getDirectAndroidResources()); transitiveResources.addTransitive(resources.getTransitiveResources()); transitiveManifests.addTransitive(resources.getTransitiveManifests()); transitiveAapt2RTxt.addTransitive(resources.getTransitiveAapt2RTxt()); transitiveSymbolsBin.addTransitive(resources.getTransitiveSymbolsBin()); transitiveCompiledSymbols.addTransitive(resources.getTransitiveCompiledSymbols()); transitiveStaticLib.addTransitive(resources.getTransitiveStaticLib()); transitiveRTxt.addTransitive(resources.getTransitiveRTxt()); } return new ResourceDependencies( neverlink, transitiveDependencies.build(), directDependencies.build(), transitiveResources.build(), transitiveManifests.build(), transitiveAapt2RTxt.build(), transitiveSymbolsBin.build(), transitiveCompiledSymbols.build(), transitiveStaticLib.build(), transitiveRTxt.build()); } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("transitiveResourceContainers", transitiveResourceContainers) .add("directResourceContainers", directResourceContainers) .add("transitiveResources", transitiveResources) .add("transitiveManifests", transitiveManifests) .add("transitiveAapt2RTxt", transitiveAapt2RTxt) .add("transitiveSymbolsBin", transitiveSymbolsBin) .add("transitiveCompiledSymbols", transitiveCompiledSymbols) .add("transitiveStaticLib", transitiveStaticLib) .add("transitiveRTxt", transitiveRTxt) .add("neverlink", neverlink) .toString(); } /** * Creates an empty ResourceDependencies instance. This is used when an AndroidResources rule is * the only resource dependency. The most common case is the AndroidTest rule. */ public static ResourceDependencies empty() { return new ResourceDependencies( false, NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER)); } private ResourceDependencies( boolean neverlink, NestedSet transitiveResourceContainers, NestedSet directResourceContainers, NestedSet transitiveResources, NestedSet transitiveManifests, NestedSet transitiveAapt2RTxt, NestedSet transitiveSymbolsBin, NestedSet transitiveCompiledSymbols, NestedSet transitiveStaticLib, NestedSet transitiveRTxt) { this.neverlink = neverlink; this.transitiveResourceContainers = transitiveResourceContainers; this.directResourceContainers = directResourceContainers; this.transitiveResources = transitiveResources; this.transitiveManifests = transitiveManifests; this.transitiveAapt2RTxt = transitiveAapt2RTxt; this.transitiveSymbolsBin = transitiveSymbolsBin; this.transitiveCompiledSymbols = transitiveCompiledSymbols; this.transitiveStaticLib = transitiveStaticLib; this.transitiveRTxt = transitiveRTxt; } /** Returns a copy of this instance with filtered resources. The original object is unchanged. */ public ResourceDependencies filter(RuleErrorConsumer errorConsumer, ResourceFilter resourceFilter) throws RuleErrorException { Optional> filteredResources = resourceFilter.maybeFilterDependencies(transitiveResources); if (!filteredResources.isPresent()) { // No filtering was done. return this; } // Note that this doesn't filter any of the dependent artifacts. This // means that if any resource changes, the corresponding actions will get // re-executed return withResources( resourceFilter.filterDependencyContainers(errorConsumer, transitiveResourceContainers), resourceFilter.filterDependencyContainers(errorConsumer, directResourceContainers), filteredResources.get()); } @VisibleForTesting ResourceDependencies withResources( NestedSet transitiveResourceContainers, NestedSet directResourceContainers, NestedSet transitiveResources) { return new ResourceDependencies( neverlink, transitiveResourceContainers, directResourceContainers, transitiveResources, transitiveManifests, transitiveAapt2RTxt, transitiveSymbolsBin, transitiveCompiledSymbols, transitiveStaticLib, transitiveRTxt); } /** * Creates a new AndroidResourcesInfo with the supplied ResourceContainer as the direct dep. * *

When a library produces a new resource container the AndroidResourcesInfo should use that * container as a the direct dependency for that library. This makes the consuming rule to * identify the new container and merge appropriately. The previous direct dependencies are then * added to the transitive dependencies. * * @param newDirectResource The new direct dependency for AndroidResourcesInfo * @return A provider with the current resources and label. */ public AndroidResourcesInfo toInfo(ValidatedAndroidResources newDirectResource) { if (neverlink) { return ResourceDependencies.empty() .toInfo( newDirectResource.getLabel(), newDirectResource.getProcessedManifest(), newDirectResource.getRTxt()); } return new AndroidResourcesInfo( newDirectResource.getLabel(), newDirectResource.getProcessedManifest(), newDirectResource.getRTxt(), NestedSetBuilder.naiveLinkOrder() .addTransitive(transitiveResourceContainers) .addTransitive(directResourceContainers) .build(), NestedSetBuilder.naiveLinkOrder() .add(newDirectResource.export()) .build(), NestedSetBuilder.naiveLinkOrder() .addTransitive(transitiveResources) .addAll(newDirectResource.getResources()) .build(), withDirectAndTransitive(newDirectResource.getManifest(), transitiveManifests), withDirectAndTransitive(newDirectResource.getAapt2RTxt(), transitiveAapt2RTxt), withDirectAndTransitive(newDirectResource.getSymbols(), transitiveSymbolsBin), withDirectAndTransitive(newDirectResource.getCompiledSymbols(), transitiveCompiledSymbols), withDirectAndTransitive(newDirectResource.getStaticLibrary(), transitiveStaticLib), withDirectAndTransitive(newDirectResource.getRTxt(), transitiveRTxt)); } /** * Create a new AndroidResourcesInfo from the dependencies of this library. * *

When a library doesn't export resources it should simply forward the current transitive and * direct resources to the consuming rule. This allows the consuming rule to make decisions about * the resource merging as if this library didn't exist. * * @param label The label of the library exporting this provider. * @return A provider with the current resources and label. */ public AndroidResourcesInfo toInfo( Label label, ProcessedAndroidManifest manifest, Artifact rTxt) { if (neverlink) { return ResourceDependencies.empty().toInfo(label, manifest, rTxt); } return new AndroidResourcesInfo( label, manifest, rTxt, transitiveResourceContainers, directResourceContainers, transitiveResources, transitiveManifests, transitiveAapt2RTxt, transitiveSymbolsBin, transitiveCompiledSymbols, transitiveStaticLib, transitiveRTxt); } private static NestedSet withDirectAndTransitive( @Nullable Artifact direct, NestedSet transitive) { NestedSetBuilder builder = NestedSetBuilder.naiveLinkOrder(); builder.addTransitive(transitive); if (direct != null) { builder.add(direct); } return builder.build(); } /** * Provides an NestedSet of the direct and transitive resources. * * @deprecated Rather than accessing the ResourceContainers, use other methods in this class to * get the specific Artifacts you need instead. */ @Deprecated public NestedSet getResourceContainers() { return NestedSetBuilder.naiveLinkOrder() .addTransitive(directResourceContainers) .addTransitive(transitiveResourceContainers) .build(); } /** * @deprecated Rather than accessing the ResourceContainers, use other methods in this class to * get the specific Artifacts you need instead. */ @Deprecated public NestedSet getTransitiveResourceContainers() { return transitiveResourceContainers; } /** * @deprecated Rather than accessing the ResourceContainers, use other methods in this class to * get the specific Artifacts you need instead. */ @Deprecated public NestedSet getDirectResourceContainers() { return directResourceContainers; } public NestedSet getTransitiveResources() { return transitiveResources; } public NestedSet getTransitiveManifests() { return transitiveManifests; } public NestedSet getTransitiveAapt2RTxt() { return transitiveAapt2RTxt; } public NestedSet getTransitiveSymbolsBin() { return transitiveSymbolsBin; } /** * @return The transitive closure of compiled symbols. Compiled symbols are zip files containing * the compiled resource output of aapt2 compile */ public NestedSet getTransitiveCompiledSymbols() { return transitiveCompiledSymbols; } public NestedSet getTransitiveStaticLib() { return transitiveStaticLib; } public NestedSet getTransitiveRTxt() { return transitiveRTxt; } }