// Copyright 2018 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.android.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; /** Wraps parsed (and, if requested, compiled) android resources. */ public class ParsedAndroidResources extends AndroidResources { private final Artifact symbols; @Nullable private final Artifact compiledSymbols; private final Label label; private final StampedAndroidManifest manifest; private final DataBindingContext dataBindingContext; public static ParsedAndroidResources parseFrom( AndroidDataContext dataContext, AndroidResources resources, StampedAndroidManifest manifest, AndroidAaptVersion aaptVersion, DataBindingContext dataBindingContext) throws InterruptedException { boolean isAapt2 = aaptVersion == AndroidAaptVersion.AAPT2; AndroidResourceParsingActionBuilder builder = new AndroidResourceParsingActionBuilder(); if (isAapt2) { // TODO(corysmith): Centralize the data binding processing and zipping into a single // action. Data binding processing needs to be triggered here as well as the merger to // avoid aapt2 from throwing an error during compilation. dataBindingContext.supplyLayoutInfo( layoutInfo -> builder.setDataBindingInfoZip( getDummyDataBindingArtifact(dataContext.getActionConstructionContext()))); } return builder .setOutput(dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_MERGED_SYMBOLS)) .setCompiledSymbolsOutput( isAapt2 ? dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_COMPILED_SYMBOLS) : null) .build( dataContext, dataBindingContext.processResources(resources), manifest, dataBindingContext); } @VisibleForTesting static Artifact getDummyDataBindingArtifact(ActionConstructionContext dataContext) { return dataContext.getUniqueDirectoryArtifact("dummydatabinding", "unused.zip"); } public static ParsedAndroidResources of( AndroidResources resources, Artifact symbols, @Nullable Artifact compiledSymbols, Label label, StampedAndroidManifest manifest, DataBindingContext dataBindingContext) { return new ParsedAndroidResources( resources, symbols, compiledSymbols, label, manifest, dataBindingContext); } ParsedAndroidResources(ParsedAndroidResources other, StampedAndroidManifest manifest) { this( other, other.symbols, other.compiledSymbols, other.label, manifest, other.dataBindingContext); } protected ParsedAndroidResources( AndroidResources resources, Artifact symbols, @Nullable Artifact compiledSymbols, Label label, StampedAndroidManifest manifest, DataBindingContext dataBindingContext) { super(resources); this.symbols = symbols; this.compiledSymbols = compiledSymbols; this.label = label; this.manifest = manifest; this.dataBindingContext = dataBindingContext; } public Artifact getSymbols() { return symbols; } @Nullable public Artifact getCompiledSymbols() { return compiledSymbols; } public Iterable getArtifacts() { return getResources(); } public Artifact getManifest() { return manifest.getManifest(); } public boolean isManifestExported() { return manifest.isExported(); } public Label getLabel() { return label; } public String getJavaPackage() { return manifest.getPackage(); } public StampedAndroidManifest getStampedManifest() { return manifest; } /** Merges this target's resources with resources from dependencies. */ MergedAndroidResources merge( AndroidDataContext dataContext, ResourceDependencies resourceDeps, AndroidAaptVersion aaptVersion) throws InterruptedException { return MergedAndroidResources.mergeFrom(dataContext, this, resourceDeps, aaptVersion); } @Override public Optional maybeFilter( RuleErrorConsumer errorConsumer, ResourceFilter resourceFilter, boolean isDependency) throws RuleErrorException { return super.maybeFilter(errorConsumer, resourceFilter, isDependency) .map( resources -> ParsedAndroidResources.of( resources, symbols, compiledSymbols, label, manifest, dataBindingContext)); } @Override public boolean equals(Object object) { if (!super.equals(object) || !(object instanceof ParsedAndroidResources)) { return false; } ParsedAndroidResources other = (ParsedAndroidResources) object; return symbols.equals(other.symbols) && Objects.equals(compiledSymbols, other.compiledSymbols) && label.equals(other.label) && manifest.equals(other.manifest); } @Override public int hashCode() { return Objects.hash(super.hashCode(), symbols, compiledSymbols, label, manifest); } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("symbols", symbols) .add("compiledSymbols", compiledSymbols) .add("label", label) .add("manifest", manifest) .toString(); } public DataBindingContext asDataBindingContext() { return dataBindingContext; } }