/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.android; import static com.facebook.buck.jvm.java.JavaLibraryClasspathProvider.getClasspathDeps; import com.facebook.buck.android.AndroidBinary.ExopackageMode; import com.facebook.buck.android.AndroidBinary.PackageType; import com.facebook.buck.android.ResourcesFilter.ResourceCompressionMode; import com.facebook.buck.android.aapt.RDotTxtEntry.RType; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.jvm.java.JavaBuckConfig; import com.facebook.buck.jvm.java.JavaLibrary; import com.facebook.buck.jvm.java.JavacFactory; import com.facebook.buck.jvm.java.JavacOptions; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.CommonDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.HasDeclaredDeps; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.coercer.BuildConfigFields; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreCollectors; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.google.common.util.concurrent.ListeningExecutorService; import java.nio.file.Path; import java.util.EnumSet; import java.util.Optional; import org.immutables.value.Value; public class AndroidInstrumentationApkDescription implements Description<AndroidInstrumentationApkDescriptionArg> { private final JavaBuckConfig javaBuckConfig; private final ProGuardConfig proGuardConfig; private final JavacOptions javacOptions; private final ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms; private final ListeningExecutorService dxExecutorService; private final CxxBuckConfig cxxBuckConfig; private final DxConfig dxConfig; public AndroidInstrumentationApkDescription( JavaBuckConfig javaBuckConfig, ProGuardConfig proGuardConfig, JavacOptions androidJavacOptions, ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms, ListeningExecutorService dxExecutorService, CxxBuckConfig cxxBuckConfig, DxConfig dxConfig) { this.javaBuckConfig = javaBuckConfig; this.proGuardConfig = proGuardConfig; this.javacOptions = androidJavacOptions; this.nativePlatforms = nativePlatforms; this.dxExecutorService = dxExecutorService; this.cxxBuckConfig = cxxBuckConfig; this.dxConfig = dxConfig; } @Override public Class<AndroidInstrumentationApkDescriptionArg> getConstructorArgType() { return AndroidInstrumentationApkDescriptionArg.class; } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, AndroidInstrumentationApkDescriptionArg args) throws NoSuchBuildTargetException { BuildRule installableApk = resolver.getRule(args.getApk()); if (!(installableApk instanceof HasInstallableApk)) { throw new HumanReadableException( "In %s, apk='%s' must be an android_binary() or apk_genrule() but was %s().", params.getBuildTarget(), installableApk.getFullyQualifiedName(), installableApk.getType()); } AndroidBinary apkUnderTest = getUnderlyingApk((HasInstallableApk) installableApk); ImmutableSortedSet<JavaLibrary> rulesToExcludeFromDex = new ImmutableSortedSet.Builder<>(Ordering.<JavaLibrary>natural()) .addAll(apkUnderTest.getRulesToExcludeFromDex()) .addAll(getClasspathDeps(apkUnderTest.getClasspathDeps())) .build(); // TODO(natthu): Instrumentation APKs should also exclude native libraries and assets from the // apk under test. AndroidPackageableCollection.ResourceDetails resourceDetails = apkUnderTest.getAndroidPackageableCollection().getResourceDetails(); ImmutableSet<BuildTarget> resourcesToExclude = ImmutableSet.copyOf( Iterables.concat( resourceDetails.getResourcesWithNonEmptyResDir(), resourceDetails.getResourcesWithEmptyResButNonEmptyAssetsDir())); Path primaryDexPath = AndroidBinary.getPrimaryDexPath(params.getBuildTarget(), params.getProjectFilesystem()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); AndroidBinaryGraphEnhancer graphEnhancer = new AndroidBinaryGraphEnhancer( params, resolver, AndroidBinary.AaptMode.AAPT1, ResourceCompressionMode.DISABLED, FilterResourcesStep.ResourceFilter.EMPTY_FILTER, /* bannedDuplicateResourceTypes */ EnumSet.noneOf(RType.class), /* resourceUnionPackage */ Optional.empty(), /* locales */ ImmutableSet.of(), args.getManifest(), PackageType.INSTRUMENTED, apkUnderTest.getCpuFilters(), /* shouldBuildStringSourceMap */ false, /* shouldPreDex */ false, primaryDexPath, DexSplitMode.NO_SPLIT, rulesToExcludeFromDex .stream() .map(BuildRule::getBuildTarget) .collect(MoreCollectors.toImmutableSet()), resourcesToExclude, /* skipCrunchPngs */ false, args.getIncludesVectorDrawables(), javaBuckConfig, JavacFactory.create(ruleFinder, javaBuckConfig, null), javacOptions, EnumSet.noneOf(ExopackageMode.class), /* buildConfigValues */ BuildConfigFields.empty(), /* buildConfigValuesFile */ Optional.empty(), /* xzCompressionLevel */ Optional.empty(), /* trimResourceIds */ false, /* keepResourcePattern */ Optional.empty(), nativePlatforms, /* nativeLibraryMergeMap */ Optional.empty(), /* nativeLibraryMergeGlue */ Optional.empty(), /* nativeLibraryMergeCodeGenerator */ Optional.empty(), /* nativeLibraryProguardConfigGenerator */ Optional.empty(), AndroidBinary.RelinkerMode.DISABLED, dxExecutorService, apkUnderTest.getManifestEntries(), cxxBuckConfig, new APKModuleGraph(targetGraph, params.getBuildTarget(), Optional.empty()), dxConfig, /* postFilterResourcesCommands */ Optional.empty()); AndroidGraphEnhancementResult enhancementResult = graphEnhancer.createAdditionalBuildables(); return new AndroidInstrumentationApk( params .copyReplacingExtraDeps(Suppliers.ofInstance(enhancementResult.getFinalDeps())) .copyAppendingExtraDeps(rulesToExcludeFromDex), ruleFinder, proGuardConfig.getProguardJarOverride(), proGuardConfig.getProguardMaxHeapSize(), proGuardConfig.getProguardAgentPath(), apkUnderTest, rulesToExcludeFromDex, enhancementResult, dxExecutorService); } private static AndroidBinary getUnderlyingApk(HasInstallableApk installable) { if (installable instanceof AndroidBinary) { return (AndroidBinary) installable; } else if (installable instanceof ApkGenrule) { return getUnderlyingApk(((ApkGenrule) installable).getInstallableApk()); } else { throw new IllegalStateException( installable.getBuildTarget().getFullyQualifiedName() + " must be backed by either an android_binary() or an apk_genrule()"); } } @BuckStyleImmutable @Value.Immutable interface AbstractAndroidInstrumentationApkDescriptionArg extends CommonDescriptionArg, HasDeclaredDeps { SourcePath getManifest(); BuildTarget getApk(); @Value.Default default boolean getIncludesVectorDrawables() { return false; } } }