blob: a3ab5e9ca0d8906960284dfcf0f184764659a291 [file] [log] [blame]
/*
* 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.model.HasBuildTarget.TO_TARGET;
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.java.Classpaths;
import com.facebook.buck.java.JavaLibrary;
import com.facebook.buck.java.JavacOptions;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.HasBuildTarget;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.ImmutableBuildRuleType;
import com.facebook.buck.rules.InstallableApk;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.coercer.BuildConfigFields;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
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 java.nio.file.Path;
import java.util.EnumSet;
public class AndroidInstrumentationApkDescription
implements Description<AndroidInstrumentationApkDescription.Arg> {
public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("android_instrumentation_apk");
private final ProGuardConfig proGuardConfig;
private final JavacOptions javacOptions;
private final ImmutableMap<AndroidBinary.TargetCpuType, NdkCxxPlatform> nativePlatforms;
public AndroidInstrumentationApkDescription(
ProGuardConfig proGuardConfig,
JavacOptions androidJavacOptions,
ImmutableMap<AndroidBinary.TargetCpuType, NdkCxxPlatform> nativePlatforms) {
this.proGuardConfig = proGuardConfig;
this.javacOptions = androidJavacOptions;
this.nativePlatforms = nativePlatforms;
}
@Override
public BuildRuleType getBuildRuleType() {
return TYPE;
}
@Override
public Arg createUnpopulatedConstructorArg() {
return new Arg();
}
@Override
public <A extends Arg> BuildRule createBuildRule(
BuildRuleParams params,
BuildRuleResolver resolver,
A args) {
BuildRule installableApk = resolver.getRule(args.apk);
if (!(installableApk instanceof InstallableApk)) {
throw new HumanReadableException(
"In %s, apk='%s' must be an android_binary() or apk_genrule() but was %s().",
params.getBuildTarget(),
installableApk.getFullyQualifiedName(),
installableApk.getType().getName());
}
AndroidBinary apkUnderTest = getUnderlyingApk((InstallableApk) installableApk);
ImmutableSortedSet<JavaLibrary> rulesToExcludeFromDex = FluentIterable.from(
ImmutableSet.<JavaLibrary>builder()
.addAll(apkUnderTest.getRulesToExcludeFromDex())
.addAll(Classpaths.getClasspathEntries(apkUnderTest.getClasspathDeps()).keySet())
.build())
.toSortedSet(HasBuildTarget.BUILD_TARGET_COMPARATOR);
// 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());
AndroidBinaryGraphEnhancer graphEnhancer = new AndroidBinaryGraphEnhancer(
params,
resolver,
ResourceCompressionMode.DISABLED,
FilterResourcesStep.ResourceFilter.EMPTY_FILTER,
/* locales */ ImmutableSet.<String>of(),
args.manifest,
PackageType.INSTRUMENTED,
apkUnderTest.getCpuFilters(),
/* shouldBuildStringSourceMap */ false,
/* shouldPreDex */ false,
primaryDexPath,
DexSplitMode.NO_SPLIT,
FluentIterable.from(rulesToExcludeFromDex).transform(TO_TARGET).toSet(),
resourcesToExclude,
/* skipCrunchPngs */ false,
javacOptions,
EnumSet.noneOf(ExopackageMode.class),
apkUnderTest.getKeystore(),
/* buildConfigValues */ BuildConfigFields.empty(),
/* buildConfigValuesFile */ Optional.<SourcePath>absent(),
nativePlatforms);
AndroidGraphEnhancementResult enhancementResult =
graphEnhancer.createAdditionalBuildables();
return new AndroidInstrumentationApk(
params.copyWithExtraDeps(Suppliers.ofInstance(enhancementResult.getFinalDeps())),
new SourcePathResolver(resolver),
proGuardConfig.getProguardJarOverride(),
proGuardConfig.getProguardMaxHeapSize(),
args.manifest,
apkUnderTest,
rulesToExcludeFromDex,
enhancementResult);
}
private static AndroidBinary getUnderlyingApk(InstallableApk 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()");
}
}
@SuppressFieldNotInitialized
public static class Arg {
public SourcePath manifest;
public BuildTarget apk;
public Optional<ImmutableSortedSet<BuildTarget>> deps;
}
}