blob: 7c96a40641c2bb41be1a5e3e1b6eaf0be0df45b5 [file] [log] [blame]
/*
* Copyright 2012-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.util.BuckConstant.BIN_PATH;
import static com.facebook.buck.util.BuckConstant.GEN_DIR;
import static com.facebook.buck.util.BuckConstant.GEN_PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.android.FilterResourcesStep.ResourceFilter;
import com.facebook.buck.android.ResourcesFilter.ResourceCompressionMode;
import com.facebook.buck.java.Keystore;
import com.facebook.buck.java.KeystoreBuilder;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.FakeBuildableContext;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.TestSourcePath;
import com.facebook.buck.step.Step;
import com.facebook.buck.testutil.MoreAsserts;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class AndroidBinaryTest {
@Test
public void testAndroidBinaryNoDx() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
// Two android_library deps, neither with an assets directory.
BuildRule libraryOne = createAndroidLibraryRule(
"//java/src/com/facebook/base:libraryOne",
ruleResolver,
null, /* resDirectory */
null, /* assetDirectory */
null /* nativeLibsDirectory */);
BuildRule libraryOneRule = ruleResolver.getRule(libraryOne.getBuildTarget());
BuildRule libraryTwo = createAndroidLibraryRule(
"//java/src/com/facebook/base:libraryTwo",
ruleResolver,
null, /* resDirectory */
null, /* assetDirectory */
null /* nativeLibsDirectory */);
BuildRule libraryTwoRule = ruleResolver.getRule(libraryTwo.getBuildTarget());
// One android_binary rule that depends on the two android_library rules.
BuildTarget binaryBuildTarget = BuildTargetFactory.newInstance(
"//java/src/com/facebook/base:apk");
ImmutableSortedSet<BuildTarget> originalDepsTargets =
ImmutableSortedSet.of(libraryOneRule.getBuildTarget(), libraryTwoRule.getBuildTarget());
BuildRule keystoreRule = addKeystoreRule(ruleResolver);
AndroidBinary androidBinary = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
binaryBuildTarget)
.setOriginalDeps(originalDepsTargets)
.setBuildTargetsToExcludeFromDex(
ImmutableSet.of(
BuildTargetFactory.newInstance("//java/src/com/facebook/base:libraryTwo")))
.setManifest(new TestSourcePath("java/src/com/facebook/base/AndroidManifest.xml"))
.setTarget("Google Inc.:Google APIs:16")
.setKeystore(keystoreRule.getBuildTarget())
.build(ruleResolver);
AndroidPackageableCollection packageableCollection =
androidBinary.getAndroidPackageableCollection();
ImmutableList.Builder<Step> commands = ImmutableList.builder();
FakeBuildableContext buildableContext = new FakeBuildableContext();
androidBinary.addProguardCommands(
packageableCollection.getClasspathEntriesToDex(),
packageableCollection.getProguardConfigs(),
commands,
buildableContext);
ImmutableSet<Path> expectedRecordedArtifacts = ImmutableSet.of(
GEN_PATH.resolve(
"java/src/com/facebook/base/__apk#aapt_package__proguard__/" +
".proguard/configuration.txt"),
GEN_PATH.resolve("java/src/com/facebook/base/__apk#aapt_package__proguard__/" +
".proguard/mapping.txt"));
assertEquals(expectedRecordedArtifacts, buildableContext.getRecordedArtifacts());
buildableContext = new FakeBuildableContext();
ImmutableList.Builder<Step> expectedSteps = ImmutableList.builder();
ProGuardObfuscateStep.create(
Optional.<Path>absent(),
"1024M",
GEN_PATH.resolve("java/src/com/facebook/base/__apk#aapt_package__proguard__/" +
"/.proguard/proguard.txt"),
ImmutableSet.<Path>of(),
ProGuardObfuscateStep.SdkProguardType.DEFAULT,
Optional.<Integer>absent(),
ImmutableMap.of(
GEN_PATH.resolve("java/src/com/facebook/base/lib__libraryOne__output/libraryOne.jar"),
GEN_PATH.resolve(
"java/src/com/facebook/base/__apk#aapt_package__proguard__/.proguard/buck-out/" +
"gen/java/src/com/facebook/base/lib__libraryOne__output/" +
"libraryOne-obfuscated.jar")),
ImmutableSet.of(
GEN_PATH.resolve("java/src/com/facebook/base/lib__libraryTwo__output/libraryTwo.jar")),
GEN_PATH.resolve("java/src/com/facebook/base/__apk#aapt_package__proguard__/.proguard"),
buildableContext,
expectedSteps);
assertEquals(expectedSteps.build(), commands.build());
assertEquals(expectedRecordedArtifacts, buildableContext.getRecordedArtifacts());
}
static BuildRule createAndroidLibraryRule(String buildTarget,
BuildRuleResolver ruleResolver,
String resDirectory,
String assetDirectory,
String nativeLibsDirectory) {
BuildTarget libraryOnebuildTarget = BuildTargetFactory.newInstance(buildTarget);
AndroidLibraryBuilder androidLibraryRuleBuilder = AndroidLibraryBuilder
.createBuilder(libraryOnebuildTarget)
.addSrc(Paths.get(buildTarget.split(":")[1] + ".java"));
if (!Strings.isNullOrEmpty(resDirectory) || !Strings.isNullOrEmpty(assetDirectory)) {
BuildTarget resourceOnebuildTarget =
BuildTargetFactory.newInstance(buildTarget + "_resources");
BuildRule androidResourceRule = ruleResolver.addToIndex(
AndroidResourceRuleBuilder.newBuilder()
.setResolver(new SourcePathResolver(ruleResolver))
.setAssets(Paths.get(assetDirectory))
.setRes(resDirectory == null ? null : Paths.get(resDirectory))
.setBuildTarget(resourceOnebuildTarget)
.build());
androidLibraryRuleBuilder.addDep(androidResourceRule.getBuildTarget());
}
if (!Strings.isNullOrEmpty(nativeLibsDirectory)) {
BuildTarget nativeLibOnebuildTarget =
BuildTargetFactory.newInstance(buildTarget + "_native_libs");
BuildRule nativeLibsRule = PrebuiltNativeLibraryBuilder.newBuilder(nativeLibOnebuildTarget)
.setNativeLibs(Paths.get(nativeLibsDirectory))
.build(ruleResolver);
ruleResolver.addToIndex(nativeLibsRule);
androidLibraryRuleBuilder.addDep(nativeLibsRule.getBuildTarget());
}
return androidLibraryRuleBuilder.build(ruleResolver);
}
@Test
public void testGetInputsToCompareToOutputIncludesManifest() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
AndroidBinaryBuilder androidBinaryRuleBuilder = AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//java/src/com/facebook:app"))
.setManifest(new TestSourcePath("java/src/com/facebook/AndroidManifest.xml"))
.setTarget("Google Inc.:Google APIs:16")
.setKeystore(addKeystoreRule(ruleResolver).getBuildTarget());
MoreAsserts.assertIterablesEquals(
"getInputsToCompareToOutput() should include manifest.",
ImmutableList.of(Paths.get("java/src/com/facebook/AndroidManifest.xml")),
androidBinaryRuleBuilder.build(ruleResolver).getInputs());
}
@Test
public void testGetInputsToCompareToOutputIncludesProguardConfig() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
AndroidBinaryBuilder androidBinaryRuleBuilder = AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//java/src/com/facebook:app"))
.setManifest(new TestSourcePath("java/src/com/facebook/AndroidManifest.xml"))
.setTarget("Google Inc.:Google APIs:16")
.setKeystore(addKeystoreRule(ruleResolver).getBuildTarget());
SourcePath proguardConfig = new TestSourcePath("java/src/com/facebook/proguard.cfg");
androidBinaryRuleBuilder.setProguardConfig(Optional.of(proguardConfig));
MoreAsserts.assertIterablesEquals(
"getInputsToCompareToOutput() should include Proguard config, if present.",
ImmutableList.of(
Paths.get("java/src/com/facebook/AndroidManifest.xml"),
Paths.get("java/src/com/facebook/proguard.cfg")),
androidBinaryRuleBuilder.build(ruleResolver).getInputs());
}
@Test
public void testGetUnsignedApkPath() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
Keystore keystore = addKeystoreRule(ruleResolver);
AndroidBinary ruleInRootDirectory = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//:fb4a"))
.setManifest(new TestSourcePath("AndroidManifest.xml"))
.setKeystore(keystore.getBuildTarget())
.setTarget("Google Inc.:Google APIs:16")
.build(ruleResolver);
assertEquals(Paths.get(GEN_DIR + "/fb4a.apk"), ruleInRootDirectory.getApkPath());
AndroidBinary ruleInNonRootDirectory = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//java/com/example:fb4a"))
.setManifest(new TestSourcePath("AndroidManifest.xml"))
.setKeystore(keystore.getBuildTarget())
.setTarget("Google Inc.:Google APIs:16")
.build(ruleResolver);
assertEquals(
Paths.get(GEN_DIR + "/java/com/example/fb4a.apk"), ruleInNonRootDirectory.getApkPath());
}
@Test
public void testGetProguardOutputFromInputClasspath() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
AndroidBinary rule = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//:fbandroid_with_dash_debug_fbsign"))
.setManifest(new TestSourcePath("AndroidManifest.xml"))
.setKeystore(addKeystoreRule(ruleResolver).getBuildTarget())
.setTarget("Google Inc.:Google APIs:16")
.build(ruleResolver);
Path proguardDir = rule.getProguardOutputFromInputClasspath(
BIN_PATH.resolve("first-party/orca/lib-base/lib__lib-base__classes"));
assertEquals(GEN_PATH.resolve(
"__fbandroid_with_dash_debug_fbsign#aapt_package__proguard__/.proguard")
.resolve(
BIN_PATH.resolve(
"first-party/orca/lib-base/lib__lib-base__classes-obfuscated.jar")),
proguardDir);
}
private void assertCommandsInOrder(List<Step> steps, List<Class<?>> expectedCommands) {
Iterable<Class<?>> filteredObservedCommands = FluentIterable
.from(steps)
.transform(new Function<Step, Class<?>>() {
@Override
public Class<?> apply(Step command) {
return command.getClass();
}
})
.filter(Predicates.in(Sets.newHashSet(expectedCommands)));
MoreAsserts.assertIterablesEquals(expectedCommands, filteredObservedCommands);
}
@Test
public void testDexingCommand() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
AndroidBinary splitDexRule = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//:fbandroid_with_dash_debug_fbsign"))
.setManifest(new TestSourcePath("AndroidManifest.xml"))
.setKeystore(addKeystoreRule(ruleResolver).getBuildTarget())
.setTarget("Google Inc.:Google APIs:16")
.setShouldSplitDex(true)
.setLinearAllocHardLimit(0)
.setPrimaryDexScenarioOverflowAllowed(true)
.setDexCompression(DexStore.JAR)
.build(ruleResolver);
Set<Path> classpath = Sets.newHashSet();
ImmutableSet.Builder<Path> secondaryDexDirectories = ImmutableSet.builder();
ImmutableList.Builder<Step> commandsBuilder = ImmutableList.builder();
Path primaryDexPath = BIN_PATH.resolve(".dex/classes.dex");
splitDexRule.addDexingSteps(
classpath,
Suppliers.<Map<String, HashCode>>ofInstance(ImmutableMap.<String, HashCode>of()),
secondaryDexDirectories,
commandsBuilder,
primaryDexPath);
assertEquals("Expected 2 new assets paths (one for metadata.txt and the other for the " +
"secondary zips)", 2, secondaryDexDirectories.build().size());
List<Step> steps = commandsBuilder.build();
assertCommandsInOrder(steps,
ImmutableList.<Class<?>>of(SplitZipStep.class, SmartDexingStep.class));
}
@Test
public void testCreateFilterResourcesStep() {
BuildRuleResolver resolver = new BuildRuleResolver();
BuildRule keystoreRule = addKeystoreRule(resolver);
AndroidBinaryBuilder builder = AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//:target"))
.setManifest(new TestSourcePath("AndroidManifest.xml"))
.setKeystore(keystoreRule.getBuildTarget())
.setTarget("Google Inc:Google APIs:16")
.setResourceFilter(new ResourceFilter(ImmutableList.of("mdpi")))
.setResourceCompressionMode(ResourceCompressionMode.ENABLED_WITH_STRINGS_AS_ASSETS);
AndroidBinary buildRule = (AndroidBinary) builder.build(resolver);
ImmutableList<Path> resourceDirectories = ImmutableList.of(Paths.get("one"), Paths.get("two"));
FilteredResourcesProvider resourcesProvider = buildRule.getEnhancementResult()
.getAaptPackageResources()
.getFilteredResourcesProvider();
assertTrue(resourcesProvider instanceof ResourcesFilter);
ImmutableList.Builder<Path> filteredDirs = ImmutableList.builder();
((ResourcesFilter) resourcesProvider)
.createFilterResourcesStep(
resourceDirectories,
/* whitelistedStringsDir */ ImmutableSet.<Path>of(),
/* locales */ ImmutableSet.<String>of(),
filteredDirs);
assertEquals(
ImmutableList.of(
Paths.get("buck-out/bin/__filtered__target#resources_filter__/0"),
Paths.get("buck-out/bin/__filtered__target#resources_filter__/1")),
filteredDirs.build());
}
private Keystore addKeystoreRule(BuildRuleResolver ruleResolver) {
BuildTarget keystoreTarget = BuildTargetFactory.newInstance("//keystore:debug");
return (Keystore) KeystoreBuilder.createBuilder(keystoreTarget)
.setStore(Paths.get("keystore/debug.keystore"))
.setProperties(Paths.get("keystore/debug.keystore.properties"))
.build(ruleResolver);
}
}