// 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.android; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import javax.annotation.Nullable; /** * Builder for creating $android_resource_merger action. The action merges resources and generates * the merged R classes for an android_library to hand off to java compilation of the library * sources. It also generates a merged resources zip file to pass on to the * $android_resource_validator action. For android_binary, see {@link * AndroidResourcesProcessorBuilder}. */ public class AndroidResourceMergingActionBuilder { // Inputs private ParsedAndroidResources primary; private ResourceDependencies dependencies; // Outputs private Artifact mergedResourcesOut; private Artifact classJarOut; private Artifact manifestOut; private @Nullable Artifact dataBindingInfoZip; // Flags private String customJavaPackage; private boolean throwOnResourceConflict; private boolean useCompiledMerge; /** * The primary resource for merging. This resource will overwrite any resource or data value in * the transitive closure. */ private AndroidResourceMergingActionBuilder withPrimary(ParsedAndroidResources primary) { this.primary = primary; return this; } public AndroidResourceMergingActionBuilder withDependencies(ResourceDependencies resourceDeps) { this.dependencies = resourceDeps; return this; } public AndroidResourceMergingActionBuilder setMergedResourcesOut(Artifact mergedResourcesOut) { this.mergedResourcesOut = mergedResourcesOut; return this; } public AndroidResourceMergingActionBuilder setClassJarOut(Artifact classJarOut) { this.classJarOut = classJarOut; return this; } public AndroidResourceMergingActionBuilder setManifestOut(Artifact manifestOut) { this.manifestOut = manifestOut; return this; } /** * The output zip for resource-processed data binding expressions (i.e. a zip of .xml files). * *

If null, data binding processing is skipped (and data binding expressions aren't allowed in * layout resources). */ public AndroidResourceMergingActionBuilder setDataBindingInfoZip(Artifact zip) { this.dataBindingInfoZip = zip; return this; } public AndroidResourceMergingActionBuilder setJavaPackage(String customJavaPackage) { this.customJavaPackage = customJavaPackage; return this; } public AndroidResourceMergingActionBuilder setThrowOnResourceConflict( boolean throwOnResourceConflict) { this.throwOnResourceConflict = throwOnResourceConflict; return this; } public AndroidResourceMergingActionBuilder setUseCompiledMerge(boolean useCompiledMerge) { this.useCompiledMerge = useCompiledMerge; return this; } private BusyBoxActionBuilder createInputsForBuilder(BusyBoxActionBuilder builder) { return builder .addAndroidJar() .addInput("--primaryManifest", primary.getManifest()) .maybeAddFlag("--packageForR", customJavaPackage) .maybeAddFlag("--throwOnResourceConflict", throwOnResourceConflict); } private void buildCompiledResourceMergingAction(BusyBoxActionBuilder builder) { Preconditions.checkNotNull(primary); createInputsForBuilder(builder) .addInput( "--primaryData", AndroidDataConverter.COMPILED_RESOURCE_CONVERTER.map(primary), Iterables.concat( primary.getArtifacts(), ImmutableList.of(primary.getCompiledSymbols()))); if (dependencies != null) { builder .addTransitiveFlag( "--data", dependencies.getTransitiveResourceContainers(), AndroidDataConverter.COMPILED_RESOURCE_CONVERTER) .addTransitiveFlag( "--directData", dependencies.getDirectResourceContainers(), AndroidDataConverter.COMPILED_RESOURCE_CONVERTER) .addTransitiveInputValues(dependencies.getTransitiveResources()) .addTransitiveInputValues(dependencies.getTransitiveCompiledSymbols()); } builder.buildAndRegister("Merging compiled Android resources", "AndroidCompiledResourceMerger"); } private void buildParsedResourceMergingAction(BusyBoxActionBuilder builder) { Preconditions.checkNotNull(primary); createInputsForBuilder(builder) .addInput( "--primaryData", AndroidDataConverter.PARSED_RESOURCE_CONVERTER.map(primary), Iterables.concat(primary.getArtifacts(), ImmutableList.of(primary.getSymbols()))); if (dependencies != null) { builder .addTransitiveFlag( "--data", dependencies.getTransitiveResourceContainers(), AndroidDataConverter.PARSED_RESOURCE_CONVERTER) .addTransitiveFlag( "--directData", dependencies.getDirectResourceContainers(), AndroidDataConverter.PARSED_RESOURCE_CONVERTER) .addTransitiveInputValues(dependencies.getTransitiveResources()) .addTransitiveInputValues(dependencies.getTransitiveSymbolsBin()); } builder.buildAndRegister("Merging Android resources", "AndroidResourceMerger"); } private void build(AndroidDataContext dataContext) { BusyBoxActionBuilder parsedMergeBuilder = BusyBoxActionBuilder.create(dataContext, "MERGE"); BusyBoxActionBuilder compiledMergeBuilder = BusyBoxActionBuilder.create(dataContext, "MERGE_COMPILED"); parsedMergeBuilder.addOutput("--resourcesOutput", mergedResourcesOut); // TODO(corysmith): Move the data binding parsing out of the merging pass to enable faster // aapt2 builds. parsedMergeBuilder.maybeAddOutput("--dataBindingInfoOut", dataBindingInfoZip); (useCompiledMerge ? compiledMergeBuilder : parsedMergeBuilder) .addOutput("--classJarOutput", classJarOut) .addLabelFlag("--targetLabel") // For now, do manifest processing to remove placeholders that aren't handled by the legacy // manifest merger. Remove this once enough users migrate over to the new manifest merger. .maybeAddOutput("--manifestOutput", manifestOut); if (useCompiledMerge) { buildCompiledResourceMergingAction(compiledMergeBuilder); } // Always make an action for merging parsed resources - the merged resources are still created // this way. buildParsedResourceMergingAction(parsedMergeBuilder); } public MergedAndroidResources build( AndroidDataContext dataContext, ParsedAndroidResources parsed) { withPrimary(parsed).build(dataContext); return MergedAndroidResources.of( parsed, mergedResourcesOut, classJarOut, dataBindingInfoZip, dependencies, parsed.getStampedManifest().withProcessedManifest(manifestOut)); } }