/** * Creates/finds the set of build rules that correspond to pre-dex'd artifacts that should be * merged to create the final classes.dex for the APK. * * <p>This method may modify {@code ruleResolver}, inserting new rules into its index. */ @VisibleForTesting BuildRule createPreDexMergeRule(UberRDotJava uberRDotJava) { ImmutableSet.Builder<DexProducedFromJavaLibrary> preDexDeps = ImmutableSet.builder(); ImmutableSet<JavaLibrary> transitiveJavaDeps = Classpaths.getClasspathEntries(originalDeps).keySet(); for (JavaLibrary javaLibrary : transitiveJavaDeps) { // If the rule has no output file (which happens when a java_library has no srcs or // resources, but export_deps is true), then there will not be anything to dx. if (javaLibrary.getPathToOutputFile() == null) { continue; } // If the rule is in the no_dx list, then do not pre-dex it. if (buildRulesToExcludeFromDex.contains(javaLibrary.getBuildTarget())) { continue; } // See whether the corresponding IntermediateDexRule has already been added to the // ruleResolver. BuildTarget originalTarget = javaLibrary.getBuildTarget(); BuildTarget preDexTarget = new BuildTarget(originalTarget.getBaseName(), originalTarget.getShortName(), DEX_FLAVOR); BuildRule preDexRule = ruleResolver.get(preDexTarget); if (preDexRule != null) { preDexDeps.add((DexProducedFromJavaLibrary) preDexRule.getBuildable()); continue; } // Create the IntermediateDexRule and add it to both the ruleResolver and preDexDeps. DexProducedFromJavaLibrary preDex = new DexProducedFromJavaLibrary(preDexTarget, javaLibrary); buildRuleAndAddToIndex( preDex, BuildRuleType.PRE_DEX, preDexTarget, ImmutableSortedSet.of(ruleResolver.get(javaLibrary.getBuildTarget()))); preDexDeps.add(preDex); } ImmutableSet<DexProducedFromJavaLibrary> allPreDexDeps = preDexDeps.build(); BuildTarget buildTargetForDexMerge = createBuildTargetWithFlavor(DEX_MERGE_FLAVOR); PreDexMerge preDexMerge = new PreDexMerge( buildTargetForDexMerge, primaryDexPath, dexSplitMode, allPreDexDeps, uberRDotJava); BuildRule preDexMergeBuildRule = buildRuleAndAddToIndex( preDexMerge, BuildRuleType.DEX_MERGE, buildTargetForDexMerge, getDexMergeDeps(uberRDotJava, allPreDexDeps)); return preDexMergeBuildRule; }
@Override public ImmutableSortedSet<BuildRule> getEnhancedDeps(BuildRuleResolver ruleResolver) { SourcePath skeletonFile = getSkeletonFile(); if (skeletonFile instanceof BuildTargetSourcePath) { BuildTarget skeletonTarget = ((BuildTargetSourcePath) skeletonFile).getTarget(); BuildRule skeletonRule = ruleResolver.get(skeletonTarget); return ImmutableSortedSet.<BuildRule>of(skeletonRule); } else { return ImmutableSortedSet.<BuildRule>of(); } }
@Override public AndroidInstrumentationApk build(BuildRuleResolver ruleResolver) { BuildRule apkRule = ruleResolver.get(this.apk); if (apkRule == null) { throw new HumanReadableException("Must specify apk for " + getBuildTarget()); } else if (!(apkRule instanceof InstallableBuildRule)) { throw new HumanReadableException( "In %s, apk='%s' must be an android_binary() or apk_genrule() but was %s().", getBuildTarget(), apkRule.getFullyQualifiedName(), apkRule.getType().getName()); } AndroidBinaryRule underlyingApk = getUnderlyingApk((InstallableBuildRule) apkRule); return new AndroidInstrumentationApk( createBuildRuleParams(ruleResolver), manifest, underlyingApk); }
/** * Tests an android_binary with multiple dependent android_library rules, each with its own assets * directory. */ @Test public void testCreateAllAssetsDirectoryWithMultipleAssetsDirectories() throws IOException { BuildRuleResolver ruleResolver = new BuildRuleResolver(); // Two android_library deps, each with an assets directory. JavaLibraryRule libraryOne = createAndroidLibraryRule( "//java/src/com/facebook/base:libraryOne", ruleResolver, null, /* resDirectory */ "java/src/com/facebook/base/assets1", null /* nativeLibsDirectory */); JavaLibraryRule libraryTwo = createAndroidLibraryRule( "//java/src/com/facebook/base:libraryTwo", ruleResolver, null, /* resDirectory */ "java/src/com/facebook/base/assets2", null /* nativeLibsDirectory */); // One android_binary rule that depends on the two android_library rules. BuildTarget binaryBuildTarget = BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk"); AndroidBinaryRule androidBinary = ruleResolver.buildAndAddToIndex( AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) .setBuildTarget(binaryBuildTarget) .addClasspathDep(libraryOne.getBuildTarget()) .addClasspathDep(libraryTwo.getBuildTarget()) .setManifest("java/src/com/facebook/base/AndroidManifest.xml") .setTarget("Google Inc.:Google APIs:16") .setKeystore(addKeystoreRule(ruleResolver)) .setPackageType("debug")); AndroidResourceRule resourceOne = (AndroidResourceRule) ruleResolver.get( BuildTargetFactory.newInstance( "//java/src/com/facebook/base:libraryOne_resources")); AndroidResourceRule resourceTwo = (AndroidResourceRule) ruleResolver.get( BuildTargetFactory.newInstance( "//java/src/com/facebook/base:libraryTwo_resources")); // Build up the parameters needed to invoke createAllAssetsDirectory(). Set<String> assetsDirectories = ImmutableSet.of(resourceOne.getAssets(), resourceTwo.getAssets()); ImmutableList.Builder<Step> commands = ImmutableList.builder(); DirectoryTraverser traverser = new DirectoryTraverser() { @Override public void traverse(DirectoryTraversal traversal) throws IOException { String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath()); if ("java/src/com/facebook/base/assets1".equals(rootPath)) { traversal.visit( new File("java/src/com/facebook/base/assets1", "guava-10.0.1-fork.dex.1.jar"), "guava-10.0.1-fork.dex.1.jar"); } else if ("java/src/com/facebook/base/assets2".equals(rootPath)) { traversal.visit( new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"), "fonts/Theinhardt-Medium.otf"); traversal.visit( new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"), "fonts/Theinhardt-Regular.otf"); } else { throw new RuntimeException("Unexpected path: " + rootPath); } } }; // Invoke createAllAssetsDirectory(), the method under test. Optional<String> allAssetsDirectory = androidBinary.createAllAssetsDirectory( assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser); // Verify that an assets/ directory will be created and passed to aapt. assertTrue(allAssetsDirectory.isPresent()); assertEquals(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__", allAssetsDirectory.get()); List<? extends Step> expectedCommands = ImmutableList.of( new MakeCleanDirectoryStep(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__"), new MkdirAndSymlinkFileStep( "java/src/com/facebook/base/assets1/guava-10.0.1-fork.dex.1.jar", BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/guava-10.0.1-fork.dex.1.jar"), new MkdirAndSymlinkFileStep( "java/src/com/facebook/base/assets2/fonts/Theinhardt-Medium.otf", BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Medium.otf"), new MkdirAndSymlinkFileStep( "java/src/com/facebook/base/assets2/fonts/Theinhardt-Regular.otf", BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Regular.otf")); MoreAsserts.assertListEquals(expectedCommands, commands.build()); }
/** * Tests an android_binary with one dependent android_library rule that contains an assets * directory. */ @Test public void testCreateAllAssetsDirectoryWithOneAssetsDirectory() throws IOException { BuildRuleResolver ruleResolver = new BuildRuleResolver(); // Two android_library deps, one of which has an assets directory. JavaLibraryRule libraryOne = createAndroidLibraryRule( "//java/src/com/facebook/base:libraryOne", ruleResolver, null, /* resDirectory */ null, /* assetDirectory */ null /* nativeLibsDirectory */); JavaLibraryRule libraryTwo = createAndroidLibraryRule( "//java/src/com/facebook/base:libraryTwo", ruleResolver, null, /* resDirectory */ "java/src/com/facebook/base/assets2", null /* nativeLibsDirectory */); AndroidResourceRule resourceOne = (AndroidResourceRule) ruleResolver.get( BuildTargetFactory.newInstance( "//java/src/com/facebook/base:libraryTwo_resources")); // One android_binary rule that depends on the two android_library rules. BuildTarget binaryBuildTarget = BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk"); AndroidBinaryRule androidBinary = ruleResolver.buildAndAddToIndex( AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) .setBuildTarget(binaryBuildTarget) .addClasspathDep(libraryOne.getBuildTarget()) .addClasspathDep(libraryTwo.getBuildTarget()) .setManifest("java/src/com/facebook/base/AndroidManifest.xml") .setTarget("Google Inc.:Google APIs:16") .setKeystore(addKeystoreRule(ruleResolver)) .setPackageType("debug")); // Build up the parameters needed to invoke createAllAssetsDirectory(). Set<String> assetsDirectories = ImmutableSet.of(resourceOne.getAssets()); ImmutableList.Builder<Step> commands = ImmutableList.builder(); DirectoryTraverser traverser = new DirectoryTraverser() { @Override public void traverse(DirectoryTraversal traversal) throws IOException { String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath()); if ("java/src/com/facebook/base/assets2".equals(rootPath)) { traversal.visit( new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"), "fonts/Theinhardt-Medium.otf"); traversal.visit( new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"), "fonts/Theinhardt-Regular.otf"); } else { throw new RuntimeException("Unexpected path: " + rootPath); } } }; // Invoke createAllAssetsDirectory(), the method under test. Optional<String> allAssetsDirectory = androidBinary.createAllAssetsDirectory( assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser); // Verify that the existing assets/ directory will be passed to aapt. assertTrue(allAssetsDirectory.isPresent()); assertEquals( "Even though there is only one assets directory, the one in " + BIN_DIR + " should be used.", androidBinary.getPathToAllAssetsDirectory(), allAssetsDirectory.get()); }