/** * Tests an android_binary with zero dependent android_library rules that contains an assets * directory. */ @Test public void testCreateAllAssetsDirectoryWithZeroAssetsDirectories() throws IOException { BuildRuleResolver ruleResolver = new BuildRuleResolver(); // Two android_library deps, neither with 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 */ null, /* assetDirectory */ 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")); // Build up the parameters needed to invoke createAllAssetsDirectory(). Set<String> assetsDirectories = ImmutableSet.of(); ImmutableList.Builder<Step> commands = ImmutableList.builder(); DirectoryTraverser traverser = new DirectoryTraverser() { @Override public void traverse(DirectoryTraversal traversal) { throw new RuntimeException("Unexpected: no assets directories to traverse!"); } }; // Invoke createAllAssetsDirectory(), the method under test. Optional<String> allAssetsDirectory = androidBinary.createAllAssetsDirectory( assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser); // Verify that no assets/ directory is used. assertFalse( "There should not be an assets/ directory to pass to aapt.", allAssetsDirectory.isPresent()); assertTrue( "There should not be any commands to build up an assets/ directory.", commands.build().isEmpty()); }
private JavaBinaryRule createSampleJavaBinaryRule(BuildRuleResolver ruleResolver) { // Create a java_binary that depends on a java_library so it is possible to create a // java_binary rule with a classpath entry and a main class. JavaLibraryRule javaLibrary = ruleResolver.buildAndAddToIndex( DefaultJavaLibraryRule.newJavaLibraryRuleBuilder( new FakeAbstractBuildRuleBuilderParams()) .setBuildTarget(BuildTargetFactory.newInstance("//java/com/facebook/util:util")) .addVisibilityPattern(BuildTargetPattern.MATCH_ALL) .addSrc("java/com/facebook/util/ManifestGenerator.java")); JavaBinaryRule javaBinary = ruleResolver.buildAndAddToIndex( JavaBinaryRule.newJavaBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) .setBuildTarget( BuildTargetFactory.newInstance("//java/com/facebook/util:ManifestGenerator")) .setMainClass("com.facebook.util.ManifestGenerator") .addDep(javaLibrary.getBuildTarget())); return javaBinary; }
@Test public void testAndroidBinaryNoDx() { BuildRuleResolver ruleResolver = new BuildRuleResolver(); // Two android_library deps, neither with 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 */ null, /* assetDirectory */ 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()) .addBuildRuleToExcludeFromDex( BuildTargetFactory.newInstance("//java/src/com/facebook/base:libraryTwo")) .setManifest("java/src/com/facebook/base/AndroidManifest.xml") .setTarget("Google Inc.:Google APIs:16") .setKeystore(addKeystoreRule(ruleResolver)) .setPackageType("debug")); DependencyGraph graph = RuleMap.createGraphFromBuildRules(ruleResolver); AndroidTransitiveDependencies transitiveDependencies = androidBinary.findTransitiveDependencies(graph); AndroidDexTransitiveDependencies dexTransitiveDependencies = androidBinary.findDexTransitiveDependencies(graph); ImmutableList.Builder<Step> commands = ImmutableList.builder(); BuildContext context = createMock(BuildContext.class); replay(context); androidBinary.addProguardCommands( context, dexTransitiveDependencies.classpathEntriesToDex, transitiveDependencies.proguardConfigs, commands, ImmutableSet.<String>of()); verify(context); MakeCleanDirectoryStep expectedClean = new MakeCleanDirectoryStep("buck-out/gen/java/src/com/facebook/base/.proguard/apk"); GenProGuardConfigStep expectedGenProguard = new GenProGuardConfigStep( "buck-out/bin/java/src/com/facebook/base/__manifest_apk__/AndroidManifest.xml", ImmutableSet.<String>of(), "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt"); ProGuardObfuscateStep expectedObfuscation = new ProGuardObfuscateStep( "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt", ImmutableSet.<String>of(), false, ImmutableMap.of( "buck-out/gen/java/src/com/facebook/base/lib__libraryOne__output/libraryOne.jar", "buck-out/gen/java/src/com/facebook/base/.proguard/apk/buck-out/gen/java/src/com/" + "facebook/base/lib__libraryOne__output/libraryOne-obfuscated.jar"), ImmutableSet.of( "buck-out/gen/java/src/com/facebook/base/lib__libraryTwo__output/libraryTwo.jar"), "buck-out/gen/java/src/com/facebook/base/.proguard/apk"); assertEquals( ImmutableList.of(expectedClean, expectedGenProguard, expectedObfuscation), commands.build()); }
/** * 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()); }
private Module createModuleForProjectConfig(ProjectConfigRule projectConfig) throws IOException { BuildRule projectRule = projectConfig.getProjectRule(); Preconditions.checkState( projectRule instanceof JavaLibraryRule || projectRule instanceof AndroidLibraryRule || projectRule instanceof AndroidResourceRule || projectRule instanceof AndroidBinaryRule || projectRule instanceof NdkLibraryRule, "project_config() does not know how to process a src_target of type %s.", projectRule.getType().getName()); LinkedHashSet<DependentModule> dependencies = Sets.newLinkedHashSet(); final BuildTarget target = projectConfig.getBuildTarget(); Module module = new Module(projectRule, target); module.name = getIntellijNameForRule(projectRule); module.isIntelliJPlugin = projectConfig.getIsIntelliJPlugin(); String relativePath = projectConfig.getBuildTarget().getBasePathWithSlash(); module.pathToImlFile = String.format("%s%s.iml", relativePath, module.name); // List the module source as the first dependency. boolean includeSourceFolder = true; // Do the tests before the sources so they appear earlier in the classpath. When tests are run, // their classpath entries may be deliberately shadowing production classpath entries. // tests folder boolean hasSourceFoldersForTestRule = addSourceFolders( module, projectConfig.getTestRule(), projectConfig.getTestsSourceRoots(), true /* isTestSource */); // test dependencies BuildRule testRule = projectConfig.getTestRule(); if (testRule != null) { walkRuleAndAdd(testRule, true /* isForTests */, dependencies, projectConfig.getSrcRule()); } // src folder boolean hasSourceFoldersForSrcRule = addSourceFolders( module, projectConfig.getSrcRule(), projectConfig.getSourceRoots(), false /* isTestSource */); // At least one of src or tests should contribute a source folder unless this is an // non-library Android project with no source roots specified. if (!hasSourceFoldersForTestRule && !hasSourceFoldersForSrcRule) { includeSourceFolder = false; } // IntelliJ expects all Android projects to have a gen/ folder, even if there is no src/ // directory specified. boolean isAndroidRule = projectRule.isAndroidRule(); if (isAndroidRule) { boolean hasSourceFolders = !module.sourceFolders.isEmpty(); module.sourceFolders.add(SourceFolder.GEN); if (!hasSourceFolders) { includeSourceFolder = true; } } // src dependencies // Note that isForTests is false even if projectRule is the project_config's test_target. walkRuleAndAdd(projectRule, false /* isForTests */, dependencies, projectConfig.getSrcRule()); String basePathWithSlash = projectConfig.getBuildTarget().getBasePathWithSlash(); // Specify another path for intellij to generate gen/ for each android module, // so that it will not disturb our glob() rules. // To specify the location of gen, Intellij requires the relative path from // the base path of current build target. module.moduleGenPath = generateRelativeGenPath(basePathWithSlash); DependentModule jdkDependency; if (isAndroidRule) { // android details if (projectRule instanceof NdkLibraryRule) { NdkLibraryRule ndkLibraryRule = (NdkLibraryRule) projectRule; module.isAndroidLibraryProject = true; module.keystorePath = null; module.nativeLibs = Paths.computeRelativePath(relativePath, ndkLibraryRule.getLibraryPath()); } else if (projectRule instanceof AndroidResourceRule) { AndroidResourceRule androidResourceRule = (AndroidResourceRule) projectRule; module.resFolder = createRelativePath(androidResourceRule.getRes(), target); module.isAndroidLibraryProject = true; module.keystorePath = null; } else if (projectRule instanceof AndroidBinaryRule) { AndroidBinaryRule androidBinaryRule = (AndroidBinaryRule) projectRule; module.resFolder = null; module.isAndroidLibraryProject = false; KeystoreProperties keystoreProperties = KeystoreProperties.createFromPropertiesFile( androidBinaryRule.getPathToKeystoreProperties(), projectFilesystem); // getKeystore() returns a path relative to the project root, but an IntelliJ module // expects the path to the keystore to be relative to the module root, so we strip off the // module path. String keystorePathRelativeToProjectRoot = keystoreProperties.getKeystore(); String keystorePath = keystorePathRelativeToProjectRoot.substring(relativePath.length()); module.keystorePath = keystorePath; } else { module.isAndroidLibraryProject = true; module.keystorePath = null; } module.hasAndroidFacet = true; module.proguardConfigPath = null; // If there is a default AndroidManifest.xml specified in .buckconfig, use it if // AndroidManifest.xml is not present in the root of the [Android] IntelliJ module. if (pathToDefaultAndroidManifest.isPresent()) { String androidManifest = basePathWithSlash + "AndroidManifest.xml"; if (!projectFilesystem.exists(androidManifest)) { String manifestPath = this.pathToDefaultAndroidManifest.get(); String rootPrefix = "//"; Preconditions.checkState( manifestPath.startsWith(rootPrefix), "Currently, we expect this option to start with '%s', " + "indicating that it is relative to the root of the repository.", rootPrefix); manifestPath = manifestPath.substring(rootPrefix.length()); String relativePathToManifest = Paths.computeRelativePath(basePathWithSlash, manifestPath); // IntelliJ requires that the path start with a slash to indicate that it is relative to // the module. module.androidManifest = "/" + relativePathToManifest; } } // List this last so that classes from modules can shadow classes in the JDK. jdkDependency = DependentModule.newInheritedJdk(); } else { module.hasAndroidFacet = false; if (module.isIntelliJPlugin()) { jdkDependency = DependentModule.newIntelliJPluginJdk(); } else { jdkDependency = DependentModule.newStandardJdk(); } } // Assign the dependencies. module.dependencies = createDependenciesInOrder(includeSourceFolder, dependencies, jdkDependency); // Annotation processing generates sources for IntelliJ to consume, but does so outside // the module directory to avoid messing up globbing. if (projectRule instanceof JavaLibraryRule) { JavaLibraryRule javaLibraryRule = (JavaLibraryRule) projectRule; AnnotationProcessingData processingData = javaLibraryRule.getAnnotationProcessingData(); String annotationGenSrc = processingData.getGeneratedSourceFolderName(); if (annotationGenSrc != null) { module.annotationGenPath = "/" + Paths.computeRelativePath(basePathWithSlash, annotationGenSrc); module.annotationGenIsForTest = !hasSourceFoldersForSrcRule; } } return module; }