| /* |
| * 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.command; |
| |
| import static com.facebook.buck.testutil.MoreAsserts.assertListEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.fail; |
| |
| import com.facebook.buck.android.AndroidBinaryRule; |
| import com.facebook.buck.android.AndroidLibraryRule; |
| import com.facebook.buck.android.AndroidResourceRule; |
| import com.facebook.buck.android.NdkLibrary; |
| import com.facebook.buck.command.Project.SourceFolder; |
| import com.facebook.buck.java.DefaultJavaLibraryRule; |
| import com.facebook.buck.java.JavaLibraryRule; |
| import com.facebook.buck.java.JavaTestRule; |
| import com.facebook.buck.java.Keystore; |
| import com.facebook.buck.java.PrebuiltJarRule; |
| import com.facebook.buck.model.BuildTarget; |
| import com.facebook.buck.model.BuildTargetFactory; |
| import com.facebook.buck.model.BuildTargetPattern; |
| import com.facebook.buck.model.SingletonBuildTargetPattern; |
| import com.facebook.buck.parser.PartialGraph; |
| import com.facebook.buck.parser.PartialGraphFactory; |
| import com.facebook.buck.rules.BuildRule; |
| import com.facebook.buck.rules.BuildRuleResolver; |
| import com.facebook.buck.rules.DependencyGraph; |
| import com.facebook.buck.rules.FakeAbstractBuildRuleBuilderParams; |
| import com.facebook.buck.rules.JavaPackageFinder; |
| import com.facebook.buck.rules.ProjectConfigRule; |
| import com.facebook.buck.step.ExecutionContext; |
| import com.facebook.buck.testutil.BuckTestConstant; |
| import com.facebook.buck.testutil.RuleMap; |
| import com.facebook.buck.util.HumanReadableException; |
| import com.facebook.buck.util.ProjectFilesystem; |
| import com.google.common.base.Function; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| |
| import org.easymock.EasyMock; |
| import org.junit.Test; |
| |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import javax.annotation.Nullable; |
| |
| public class ProjectTest { |
| |
| private static final String PATH_TO_GUAVA_JAR = "third_party/guava/guava-10.0.1.jar"; |
| |
| @SuppressWarnings("PMD.UnusedPrivateField") |
| private PrebuiltJarRule guava; |
| |
| /** |
| * Creates a PartialGraph with two android_binary rules, each of which depends on the same |
| * android_library. The difference between the two is that one lists Guava in its no_dx list and |
| * the other does not. |
| * <p> |
| * The PartialGraph also includes three project_config rules: one for the android_library, and one |
| * for each of the android_binary rules. |
| */ |
| public ProjectWithModules createPartialGraphForTesting( |
| @Nullable JavaPackageFinder javaPackageFinder) throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| // java_library //buck-out/android/com/facebook:R |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//buck-out/android/com/facebook:R")) |
| .addSrc("buck-out/android/com/facebook/R.java") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // prebuilt_jar //third_party/guava:guava |
| guava = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/guava:guava")) |
| .setBinaryJar(PATH_TO_GUAVA_JAR) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // android_resouce android_res/base:res |
| ruleResolver.buildAndAddToIndex( |
| AndroidResourceRule.newAndroidResourceRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//android_res/base:res")) |
| .setRes("android_res/base/res") |
| .setRDotJavaPackage("com.facebook") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // project_config android_res/base:res |
| ProjectConfigRule projectConfigRuleForResource = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//android_res/base:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//android_res/base:res"))) |
| .setSrcRoots(ImmutableList.of("res"))); |
| |
| // java_library //java/src/com/facebook/grandchild:grandchild |
| BuildTarget grandchild = BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/grandchild:grandchild"); |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(grandchild) |
| .addSrc("Grandchild.java") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // java_library //java/src/com/facebook/child:child |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/child:child")) |
| .addSrc("Child.java") |
| .addDep(grandchild) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // java_library //java/src/com/facebook/exportlib:exportlib |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/exportlib:exportlib")) |
| .addSrc("ExportLib.java") |
| .addDep(BuildTargetFactory.newInstance("//third_party/guava:guava")) |
| .setExportDeps(true) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // android_library //java/src/com/facebook/base:base |
| ruleResolver.buildAndAddToIndex( |
| AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/src/com/facebook/base:base")) |
| .addSrc("Base.java") |
| .addDep(BuildTargetFactory.newInstance("//buck-out/android/com/facebook:R")) |
| .addDep(BuildTargetFactory.newInstance("//java/src/com/facebook/exportlib:exportlib")) |
| .addDep(BuildTargetFactory.newInstance("//java/src/com/facebook/child:child")) |
| .addDep(BuildTargetFactory.newInstance("//android_res/base:res")) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // project_config //java/src/com/facebook/base:project_config |
| ProjectConfigRule projectConfigRuleForLibrary = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/base:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/base:base"))) |
| .setSrcRoots(ImmutableList.of("src", "src-gen"))); |
| |
| ProjectConfigRule projectConfigRuleForExportLibrary = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/exportlib:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance( |
| "//java/src/com/facebook/exportlib:exportlib"))) |
| .setSrcRoots(ImmutableList.of("src"))); |
| |
| // keystore //keystore:debug |
| BuildTarget keystoreTarget = BuildTargetFactory.newInstance("//keystore:debug"); |
| ruleResolver.buildAndAddToIndex( |
| Keystore.newKeystoreBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(keystoreTarget) |
| .setStore("keystore/debug.keystore") |
| .setProperties("keystore/debug.keystore.properties") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // android_binary //foo:app |
| ruleResolver.buildAndAddToIndex( |
| AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//foo:app")) |
| .addClasspathDep(BuildTargetFactory.newInstance("//java/src/com/facebook/base:base")) |
| .setManifest("foo/AndroidManifest.xml") |
| .setTarget("Google Inc.:Google APIs:16") |
| .setKeystore(keystoreTarget) |
| .addBuildRuleToExcludeFromDex(BuildTargetFactory.newInstance("//third_party/guava:guava"))); |
| |
| // project_config //foo:project_config |
| ProjectConfigRule projectConfigRuleUsingNoDx = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//foo:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//foo:app")))); |
| |
| // android_binary //bar:app |
| ruleResolver.buildAndAddToIndex( |
| AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//bar:app")) |
| .addClasspathDep(BuildTargetFactory.newInstance("//java/src/com/facebook/base:base")) |
| .setManifest("foo/AndroidManifest.xml") |
| .setTarget("Google Inc.:Google APIs:16") |
| .setKeystore(keystoreTarget)); |
| |
| // project_config //bar:project_config |
| ProjectConfigRule projectConfigRule = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//bar:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//bar:app")))); |
| |
| return getModulesForPartialGraph(ruleResolver, |
| ImmutableList.of( |
| projectConfigRuleForExportLibrary, |
| projectConfigRuleForLibrary, |
| projectConfigRuleForResource, |
| projectConfigRuleUsingNoDx, |
| projectConfigRule), |
| javaPackageFinder); |
| } |
| |
| @Test |
| public void testGenerateRelativeGenPath() { |
| String basePathOfModuleWithSlash = "android_res/com/facebook/gifts/"; |
| Path expectedRelativePathToGen = |
| java.nio.file.Paths.get("/../../../../buck-out/android/android_res/com/facebook/gifts/gen"); |
| assertEquals( |
| expectedRelativePathToGen, Project.generateRelativeGenPath(basePathOfModuleWithSlash)); |
| } |
| |
| /** |
| * This is an important test that verifies that the {@code no_dx} argument for an |
| * {@code android_binary} is handled appropriately when generating an IntelliJ project. |
| */ |
| @Test |
| public void testProject() throws IOException { |
| JavaPackageFinder javaPackageFinder = EasyMock.createMock(JavaPackageFinder.class); |
| EasyMock.expect(javaPackageFinder.findJavaPackageForPath("foo/module_foo.iml")).andReturn(""); |
| EasyMock.expect(javaPackageFinder.findJavaPackageForPath("bar/module_bar.iml")).andReturn(""); |
| EasyMock.replay(javaPackageFinder); |
| |
| ProjectWithModules projectWithModules = createPartialGraphForTesting(javaPackageFinder); |
| Project project = projectWithModules.project; |
| PartialGraph partialGraph = project.getPartialGraph(); |
| List<Module> modules = projectWithModules.modules; |
| |
| assertEquals("Should be one module for the java_library, one for the android_library, " + |
| "one module for the android_resource, and one for each android_binary", |
| 5, |
| modules.size()); |
| |
| // Check the values of the module that corresponds to the android_library. |
| Module javaLibraryModule = modules.get(0); |
| assertSame(getRuleById("//java/src/com/facebook/exportlib:exportlib", partialGraph), |
| javaLibraryModule.srcRule); |
| assertEquals("module_java_src_com_facebook_exportlib", javaLibraryModule.name); |
| assertEquals("java/src/com/facebook/exportlib/module_java_src_com_facebook_exportlib.iml", |
| javaLibraryModule.pathToImlFile); |
| assertListEquals( |
| ImmutableList.of(SourceFolder.SRC), |
| javaLibraryModule.sourceFolders); |
| |
| // Check the dependencies. |
| DependentModule inheritedJdk = DependentModule.newInheritedJdk(); |
| DependentModule guavaAsProvidedDep = DependentModule.newLibrary( |
| guava.getBuildTarget(), "third_party_guava_guava_10_0_1_jar"); |
| guavaAsProvidedDep.scope = "PROVIDED"; |
| |
| assertListEquals( |
| ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| guavaAsProvidedDep, |
| DependentModule.newStandardJdk()), |
| javaLibraryModule.dependencies); |
| |
| // Check the values of the module that corresponds to the android_library. |
| Module androidLibraryModule = modules.get(1); |
| assertSame(getRuleById("//java/src/com/facebook/base:base", partialGraph), |
| androidLibraryModule.srcRule); |
| assertEquals("module_java_src_com_facebook_base", androidLibraryModule.name); |
| assertEquals("java/src/com/facebook/base/module_java_src_com_facebook_base.iml", |
| androidLibraryModule.pathToImlFile); |
| assertListEquals( |
| ImmutableList.of( |
| SourceFolder.SRC, |
| new SourceFolder("file://$MODULE_DIR$/src-gen", false /* isTestSource */), |
| SourceFolder.GEN), |
| androidLibraryModule.sourceFolders); |
| assertEquals(Boolean.TRUE, androidLibraryModule.hasAndroidFacet); |
| assertEquals(Boolean.TRUE, androidLibraryModule.isAndroidLibraryProject); |
| assertEquals(null, androidLibraryModule.proguardConfigPath); |
| assertEquals(null, androidLibraryModule.resFolder); |
| |
| // Check the dependencies. |
| DependentModule androidResourceAsProvidedDep = DependentModule.newModule( |
| BuildTargetFactory.newInstance("//android_res/base:res"), |
| "module_android_res_base"); |
| |
| DependentModule childAsProvidedDep = DependentModule.newModule( |
| BuildTargetFactory.newInstance("//java/src/com/facebook/child:child"), |
| "module_java_src_com_facebook_child"); |
| |
| DependentModule exportDepsAsProvidedDep = DependentModule.newModule( |
| BuildTargetFactory.newInstance("//java/src/com/facebook/exportlib:exportlib"), |
| "module_java_src_com_facebook_exportlib"); |
| |
| assertListEquals( |
| ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| guavaAsProvidedDep, |
| androidResourceAsProvidedDep, |
| childAsProvidedDep, |
| exportDepsAsProvidedDep, |
| inheritedJdk), |
| androidLibraryModule.dependencies); |
| |
| // Check the values of the module that corresponds to the android_binary that uses no_dx. |
| Module androidResourceModule = modules.get(2); |
| assertSame(getRuleById("//android_res/base:res", partialGraph), androidResourceModule.srcRule); |
| |
| assertEquals("/res", androidResourceModule.resFolder); |
| |
| // Check the values of the module that corresponds to the android_binary that uses no_dx. |
| Module androidBinaryModuleNoDx = modules.get(3); |
| assertSame(getRuleById("//foo:app", partialGraph), androidBinaryModuleNoDx.srcRule); |
| assertEquals("module_foo", androidBinaryModuleNoDx.name); |
| assertEquals("foo/module_foo.iml", androidBinaryModuleNoDx.pathToImlFile); |
| |
| assertListEquals(ImmutableList.of(SourceFolder.GEN), androidBinaryModuleNoDx.sourceFolders); |
| assertEquals(Boolean.TRUE, androidBinaryModuleNoDx.hasAndroidFacet); |
| assertEquals(Boolean.FALSE, androidBinaryModuleNoDx.isAndroidLibraryProject); |
| assertEquals(null, androidBinaryModuleNoDx.proguardConfigPath); |
| assertEquals(null, androidBinaryModuleNoDx.resFolder); |
| assertEquals("../keystore/debug.keystore", androidBinaryModuleNoDx.keystorePath); |
| |
| // Check the dependencies. |
| DependentModule grandchildAsProvidedDep = DependentModule.newModule( |
| BuildTargetFactory.newInstance("//java/src/com/facebook/grandchild:grandchild"), |
| "module_java_src_com_facebook_grandchild" |
| ); |
| |
| DependentModule androidLibraryDep = DependentModule.newModule( |
| androidLibraryModule.srcRule.getBuildTarget(), "module_java_src_com_facebook_base"); |
| assertEquals( |
| ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| guavaAsProvidedDep, |
| androidLibraryDep, |
| androidResourceAsProvidedDep, |
| childAsProvidedDep, |
| exportDepsAsProvidedDep, |
| grandchildAsProvidedDep, |
| inheritedJdk), |
| androidBinaryModuleNoDx.dependencies); |
| |
| // Check the values of the module that corresponds to the android_binary with an empty no_dx. |
| Module androidBinaryModuleEmptyNoDx = modules.get(4); |
| assertSame(getRuleById("//bar:app", partialGraph), androidBinaryModuleEmptyNoDx.srcRule); |
| assertEquals("module_bar", androidBinaryModuleEmptyNoDx.name); |
| assertEquals("bar/module_bar.iml", androidBinaryModuleEmptyNoDx.pathToImlFile); |
| assertListEquals( |
| ImmutableList.of(SourceFolder.GEN), androidBinaryModuleEmptyNoDx.sourceFolders); |
| assertEquals(Boolean.TRUE, androidBinaryModuleEmptyNoDx.hasAndroidFacet); |
| assertEquals(Boolean.FALSE, androidBinaryModuleEmptyNoDx.isAndroidLibraryProject); |
| assertEquals(null, androidBinaryModuleEmptyNoDx.proguardConfigPath); |
| assertEquals(null, androidBinaryModuleEmptyNoDx.resFolder); |
| assertEquals("../keystore/debug.keystore", androidBinaryModuleEmptyNoDx.keystorePath); |
| |
| // Check the dependencies. |
| DependentModule guavaAsCompiledDep = DependentModule.newLibrary( |
| guava.getBuildTarget(), "third_party_guava_guava_10_0_1_jar"); |
| assertEquals("Important that Guava is listed as a 'COMPILED' dependency here because it is " |
| + "only listed as a 'PROVIDED' dependency earlier.", |
| ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| guavaAsCompiledDep, |
| androidLibraryDep, |
| androidResourceAsProvidedDep, |
| childAsProvidedDep, |
| exportDepsAsProvidedDep, |
| grandchildAsProvidedDep, |
| inheritedJdk), |
| androidBinaryModuleEmptyNoDx.dependencies); |
| |
| // Check that the correct data was extracted to populate the .idea/libraries directory. |
| BuildRule guava = getRuleById("//third_party/guava:guava", partialGraph); |
| assertSame(guava, Iterables.getOnlyElement(project.getLibraryJars())); |
| } |
| |
| @Test |
| public void testPrebuiltJarIncludesDeps() throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| // Build up a the graph that corresponds to: |
| // |
| // android_library( |
| // name = 'example', |
| // deps = [ |
| // ':easymock', |
| // ], |
| // ) |
| // |
| // prebuilt_jar( |
| // name = 'easymock', |
| // binary_jar = 'easymock.jar', |
| // deps = [ |
| // ':cglib', |
| // ':objenesis', |
| // ], |
| // ) |
| // |
| // prebuilt_jar( |
| // name = 'cglib', |
| // binary_jar = 'cglib.jar', |
| // ) |
| // |
| // prebuilt_jar( |
| // name = 'objenesis', |
| // binary_jar = 'objenesis.jar', |
| // ) |
| // |
| // project_config( |
| // src_target = ':example', |
| // ) |
| PrebuiltJarRule cglib = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/easymock:cglib")) |
| .setBinaryJar("third_party/java/easymock/cglib.jar")); |
| |
| PrebuiltJarRule objenesis = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/easymock:objenesis")) |
| .setBinaryJar("third_party/java/easymock/objenesis.jar")); |
| |
| PrebuiltJarRule easymock = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/easymock:easymock")) |
| .setBinaryJar("third_party/java/easymock/easymock.jar") |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/easymock:cglib")) |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/easymock:objenesis"))); |
| |
| BuildTarget easyMockExampleTarget = BuildTargetFactory.newInstance( |
| "//third_party/java/easymock:example"); |
| ruleResolver.buildAndAddToIndex( |
| AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(easyMockExampleTarget) |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/easymock:easymock"))); |
| |
| ProjectConfigRule projectConfig = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget( |
| BuildTargetFactory.newInstance("//third_party/java/easymock:project_config")) |
| .setSrcTarget(Optional.of(easyMockExampleTarget))); |
| |
| ProjectWithModules projectWithModules = getModulesForPartialGraph(ruleResolver, |
| ImmutableList.of(projectConfig), |
| null /* javaPackageFinder */); |
| List<Module> modules = projectWithModules.modules; |
| |
| // Verify that the single Module that is created transitively includes all JAR files. |
| assertEquals("Should be one module for the android_library", 1, modules.size()); |
| Module androidLibraryModule = Iterables.getOnlyElement(modules); |
| assertListEquals(ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| DependentModule.newLibrary( |
| easymock.getBuildTarget(), |
| "third_party_java_easymock_easymock_jar"), |
| DependentModule.newLibrary( |
| cglib.getBuildTarget(), |
| "third_party_java_easymock_cglib_jar"), |
| DependentModule.newLibrary( |
| objenesis.getBuildTarget(), |
| "third_party_java_easymock_objenesis_jar"), |
| DependentModule.newInheritedJdk()), |
| androidLibraryModule.dependencies); |
| } |
| |
| @Test |
| public void testIfModuleIsBothTestAndCompileDepThenTreatAsCompileDep() throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| // Create a java_library() and a java_test() that both depend on Guava. |
| // When they are part of the same project_config() rule, then the resulting module should |
| // include Guava as scope="COMPILE" in IntelliJ. |
| PrebuiltJarRule guava = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/guava:guava")) |
| .setBinaryJar("third_party/java/guava.jar") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:base")) |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/guava:guava"))); |
| |
| ruleResolver.buildAndAddToIndex( |
| JavaTestRule.newJavaTestRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:tests")) |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/guava:guava"))); |
| |
| ProjectConfigRule projectConfig = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//java/com/example/base:base"))) |
| .setTestTarget(Optional.of(BuildTargetFactory.newInstance("//java/com/example/base:tests"))) |
| .setTestRoots(ImmutableList.of("tests"))); |
| |
| ProjectWithModules projectWithModules = getModulesForPartialGraph(ruleResolver, |
| ImmutableList.of(projectConfig), |
| null /* javaPackageFinder */); |
| List<Module> modules = projectWithModules.modules; |
| assertEquals(1, modules.size()); |
| Module comExampleBaseModule = Iterables.getOnlyElement(modules); |
| |
| assertListEquals(ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| DependentModule.newLibrary(guava.getBuildTarget(), "third_party_java_guava_jar"), |
| DependentModule.newStandardJdk()), |
| comExampleBaseModule.dependencies); |
| } |
| |
| /** |
| * In the context of Robolectric, httpcore-4.0.1.jar needs to be loaded before the android.jar |
| * associated with the Android SDK. Both httpcore-4.0.1.jar and android.jar define |
| * org.apache.http.params.BasicHttpParams; however, only httpcore-4.0.1.jar contains a real |
| * implementation of BasicHttpParams whereas android.jar contains a stub implementation of |
| * BasicHttpParams. |
| * <p> |
| * One way to fix this problem would be to "tag" httpcore-4.0.1.jar to indicate that it must |
| * appear before the Android SDK (or anything that transitively depends on the Android SDK) when |
| * listing dependencies for IntelliJ. This would be a giant kludge to the prebuilt_jar rule, so |
| * instead we just list jars before modules within an <orderEntry scope="TEST"/> or an |
| * <orderEntry scope="COMPILE"/> group. |
| */ |
| @Test |
| public void testThatJarsAreListedBeforeModules() throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| JavaLibraryRule supportV4 = ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/android/support/v4:v4")) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| PrebuiltJarRule httpCore = ruleResolver.buildAndAddToIndex( |
| PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/httpcore:httpcore")) |
| .setBinaryJar("httpcore-4.0.1.jar") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| // The support-v4 library is loaded as a java_library() rather than a prebuilt_jar() because it |
| // contains our local changes to the library. |
| BuildTarget robolectricTarget = |
| BuildTargetFactory.newInstance("//third_party/java/robolectric:robolectric"); |
| ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(robolectricTarget) |
| .addDep(BuildTargetFactory.newInstance("//java/com/android/support/v4:v4")) |
| .addDep(BuildTargetFactory.newInstance("//third_party/java/httpcore:httpcore"))); |
| |
| ProjectConfigRule projectConfig = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance( |
| "//third_party/java/robolectric:project_config")) |
| .setSrcTarget(Optional.of(robolectricTarget)) |
| .setSrcRoots(ImmutableList.of("src/main/java"))); |
| |
| ProjectWithModules projectWithModules = getModulesForPartialGraph(ruleResolver, |
| ImmutableList.of(projectConfig), |
| null /* javaPackageFinder */); |
| List<Module> modules = projectWithModules.modules; |
| assertEquals("Should be one module for the android_library", 1, modules.size()); |
| Module robolectricModule = Iterables.getOnlyElement(modules); |
| |
| assertListEquals( |
| "It is imperative that httpcore-4.0.1.jar be listed before the support v4 library, " + |
| "or else when robolectric is listed as a dependency, " + |
| "org.apache.http.params.BasicHttpParams will be loaded from android.jar instead of " + |
| "httpcore-4.0.1.jar.", |
| ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| DependentModule.newLibrary(httpCore.getBuildTarget(), "httpcore_4_0_1_jar"), |
| DependentModule.newModule( |
| supportV4.getBuildTarget(), "module_java_com_android_support_v4"), |
| DependentModule.newStandardJdk()), |
| robolectricModule.dependencies); |
| } |
| |
| @Test |
| public void testCreatePathToProjectDotPropertiesFileForModule() { |
| Module rootModule = new Module(null /* buildRule */, |
| BuildTargetFactory.newInstance("//:project_config")); |
| rootModule.pathToImlFile = "fb4a.iml"; |
| assertEquals("project.properties", Project.createPathToProjectDotPropertiesFileFor(rootModule)); |
| |
| Module someModule = new Module(null /* buildRule */, |
| BuildTargetFactory.newInstance("//java/com/example/base:project_config")); |
| someModule.pathToImlFile = "java/com/example/base/base.iml"; |
| assertEquals("java/com/example/base/project.properties", |
| Project.createPathToProjectDotPropertiesFileFor(someModule)); |
| } |
| |
| /** |
| * A project_config()'s src_roots argument can be {@code None}, {@code []}, or a non-empty array. |
| * Each of these should be treated differently. |
| */ |
| @Test |
| public void testSrcRoots() throws IOException { |
| // Create a project_config() with src_roots=None. |
| BuildRuleResolver ruleResolver1 = new BuildRuleResolver(); |
| ruleResolver1.buildAndAddToIndex( |
| AndroidResourceRule.newAndroidResourceRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//resources/com/example:res"))); |
| ProjectConfigRule projectConfigNullSrcRoots = ruleResolver1.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//resources/com/example:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//resources/com/example:res"))) |
| .setSrcRoots(null)); |
| ProjectWithModules projectWithModules1 = getModulesForPartialGraph(ruleResolver1, |
| ImmutableList.of(projectConfigNullSrcRoots), |
| null /* javaPackageFinder */); |
| |
| // Verify that the correct source folders are created. |
| assertEquals(1, projectWithModules1.modules.size()); |
| Module moduleNoJavaSource = projectWithModules1.modules.get(0); |
| assertListEquals( |
| "Only source tmp should be gen/ when setSrcRoots(null) is specified.", |
| ImmutableList.of(SourceFolder.GEN), |
| moduleNoJavaSource.sourceFolders); |
| |
| // Create a project_config() with src_roots=[]. |
| BuildRuleResolver ruleResolver2 = new BuildRuleResolver(); |
| ruleResolver2.buildAndAddToIndex( |
| AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:base"))); |
| ProjectConfigRule inPackageProjectConfig = ruleResolver2.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//java/com/example/base:base"))) |
| .setSrcRoots(ImmutableList.<String>of())); |
| |
| // Verify that the correct source folders are created. |
| JavaPackageFinder javaPackageFinder = EasyMock.createMock(JavaPackageFinder.class); |
| EasyMock.expect(javaPackageFinder.findJavaPackageForPath( |
| "java/com/example/base/module_java_com_example_base.iml")).andReturn("com.example.base"); |
| EasyMock.replay(javaPackageFinder); |
| ProjectWithModules projectWithModules2 = getModulesForPartialGraph(ruleResolver2, |
| ImmutableList.of(inPackageProjectConfig), |
| javaPackageFinder); |
| EasyMock.verify(javaPackageFinder); |
| assertEquals(1, projectWithModules2.modules.size()); |
| Module moduleWithPackagePrefix = projectWithModules2.modules.get(0); |
| assertListEquals( |
| "The current directory should be a source tmp with a package prefix " + |
| "as well as the gen/ directory.", |
| ImmutableList.of( |
| new SourceFolder("file://$MODULE_DIR$", false /* isTestSource */, "com.example.base"), |
| SourceFolder.GEN), |
| moduleWithPackagePrefix.sourceFolders); |
| |
| // Create a project_config() with src_roots=['src']. |
| BuildRuleResolver ruleResolver3 = new BuildRuleResolver(); |
| ruleResolver3.buildAndAddToIndex( |
| AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:base"))); |
| ProjectConfigRule hasSrcFolderProjectConfig = ruleResolver3.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example/base:project_config")) |
| .setSrcTarget(Optional.of(BuildTargetFactory.newInstance("//java/com/example/base:base"))) |
| .setSrcRoots(ImmutableList.of("src"))); |
| ProjectWithModules projectWithModules3 = getModulesForPartialGraph(ruleResolver3, |
| ImmutableList.of(hasSrcFolderProjectConfig), |
| null /* javaPackageFinder */); |
| |
| // Verify that the correct source folders are created. |
| assertEquals(1, projectWithModules3.modules.size()); |
| Module moduleHasSrcFolder = projectWithModules3.modules.get(0); |
| assertListEquals( |
| "Both src/ and gen/ should be source folders.", |
| ImmutableList.of( |
| new SourceFolder("file://$MODULE_DIR$/src", false /* isTestSource */), |
| SourceFolder.GEN), |
| moduleHasSrcFolder.sourceFolders); |
| } |
| |
| private static class ProjectWithModules { |
| private final Project project; |
| private final ImmutableList<Module> modules; |
| private ProjectWithModules(Project project, ImmutableList<Module> modules) { |
| this.project = project; |
| this.modules = modules; |
| } |
| } |
| |
| private ProjectWithModules getModulesForPartialGraph( |
| BuildRuleResolver ruleResolver, |
| ImmutableList<ProjectConfigRule> projectConfigs, |
| @Nullable JavaPackageFinder javaPackageFinder) throws IOException { |
| if (javaPackageFinder == null) { |
| javaPackageFinder = EasyMock.createMock(JavaPackageFinder.class); |
| } |
| |
| DependencyGraph graph = RuleMap.createGraphFromBuildRules(ruleResolver); |
| List<BuildTarget> targets = ImmutableList.copyOf(Iterables.transform(projectConfigs, |
| new Function<ProjectConfigRule, BuildTarget>() { |
| |
| @Override |
| public BuildTarget apply(ProjectConfigRule rule) { |
| return rule.getBuildTarget(); |
| } |
| })); |
| PartialGraph partialGraph = PartialGraphFactory.newInstance(graph, targets); |
| |
| // Create the Project. |
| ExecutionContext executionContext = EasyMock.createMock(ExecutionContext.class); |
| ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class); |
| |
| Properties keystoreProperties = new Properties(); |
| keystoreProperties.put("key.alias", "androiddebugkey"); |
| keystoreProperties.put("key.store.password", "android"); |
| keystoreProperties.put("key.alias.password", "android"); |
| EasyMock.expect(projectFilesystem.readPropertiesFile( |
| "keystore/debug.keystore.properties")) |
| .andReturn(keystoreProperties).anyTimes(); |
| |
| ImmutableMap<String, String> basePathToAliasMap = ImmutableMap.of(); |
| Project project = new Project( |
| partialGraph, |
| basePathToAliasMap, |
| javaPackageFinder, |
| executionContext, |
| projectFilesystem, |
| /* pathToDefaultAndroidManifest */ Optional.<String>absent(), |
| /* pathToPostProcessScript */ Optional.<String>absent(), |
| BuckTestConstant.PYTHON_INTERPRETER); |
| |
| // Execute Project's business logic. |
| EasyMock.replay(executionContext, projectFilesystem); |
| List<Module> modules = project.createModulesForProjectConfigs(); |
| EasyMock.verify(executionContext, projectFilesystem); |
| |
| return new ProjectWithModules(project, ImmutableList.copyOf(modules)); |
| } |
| |
| private static BuildRule getRuleById(String id, PartialGraph graph) { |
| String[] parts = id.split(":"); |
| BuildRule rule = graph.getDependencyGraph().findBuildRuleByTarget( |
| new BuildTarget(parts[0], parts[1])); |
| Preconditions.checkNotNull(rule, "No rule for %s", id); |
| return rule; |
| } |
| |
| @Test |
| public void testNdkLibraryHasCorrectPath() throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| // Build up a the graph that corresponds to: |
| // |
| // ndk_library( |
| // name = 'foo-jni' |
| // ) |
| // |
| // project_config( |
| // src_target = ':foo-jni', |
| // ) |
| |
| BuildTarget fooJni = BuildTargetFactory.newInstance("//third_party/java/foo/jni:foo-jni"); |
| BuildRule ndkLibrary = ruleResolver.buildAndAddToIndex( |
| NdkLibrary.newNdkLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(fooJni) |
| .addSrc("Android.mk") |
| .addVisibilityPattern(new SingletonBuildTargetPattern("//third_party/java/foo:foo"))); |
| |
| ProjectConfigRule ndkProjectConfig = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//third_party/java/foo/jni:project_config")) |
| .setSrcTarget(Optional.of(fooJni))); |
| |
| ProjectWithModules projectWithModules = getModulesForPartialGraph(ruleResolver, |
| ImmutableList.of(ndkProjectConfig), |
| null /* javaPackageFinder */); |
| List<Module> modules = projectWithModules.modules; |
| |
| assertEquals("Should be one module for the ndk_library.", 1, modules.size()); |
| Module androidLibraryModule = Iterables.getOnlyElement(modules); |
| assertListEquals(ImmutableList.of( |
| DependentModule.newSourceFolder(), |
| DependentModule.newInheritedJdk()), |
| androidLibraryModule.dependencies); |
| assertEquals( |
| String.format("../../../../%s", ((NdkLibrary) ndkLibrary.getBuildable()).getLibraryPath()), |
| androidLibraryModule.nativeLibs); |
| } |
| |
| @Test |
| public void shouldThrowAnExceptionIfAModuleIsMissingADependencyWhenGeneratingProjectFiles() |
| throws IOException { |
| BuildRuleResolver ruleResolver = new BuildRuleResolver(); |
| |
| DefaultJavaLibraryRule ex1 = ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//example/parent:ex1")) |
| .addSrc("DoesNotExist.java") |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| DefaultJavaLibraryRule ex2 = ruleResolver.buildAndAddToIndex( |
| DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//example/child:ex2")) |
| .addSrc("AlsoDoesNotExist.java") |
| .addDep(ex1.getBuildTarget()) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| DefaultJavaLibraryRule tests = ruleResolver.buildAndAddToIndex( |
| JavaTestRule.newJavaTestRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//example/child:tests")) |
| .addSrc("SomeTestFile.java") |
| .addDep(ex2.getBuildTarget()) |
| .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); |
| |
| ProjectConfigRule config = ruleResolver.buildAndAddToIndex( |
| ProjectConfigRule.newProjectConfigRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) |
| .setBuildTarget(BuildTargetFactory.newInstance("//example/child:config")) |
| .setSrcTarget(Optional.of(ex2.getBuildTarget())) |
| .setTestTarget(Optional.of(tests.getBuildTarget()))); |
| |
| ProjectWithModules projectWithModules = getModulesForPartialGraph( |
| ruleResolver, ImmutableList.of(config), null); |
| |
| Module module = Iterables.getOnlyElement(projectWithModules.modules); |
| List<Module> modules = projectWithModules.project.createModulesForProjectConfigs(); |
| Map<String, Module> map = projectWithModules.project.buildNameToModuleMap(modules); |
| |
| try { |
| projectWithModules.project.writeProjectDotPropertiesFile(module, map); |
| fail("Should have thrown a HumanReadableException"); |
| } catch (HumanReadableException e) { |
| assertEquals("You must define a project_config() in example/child/BUCK containing " + |
| "//example/parent:ex1. The project_config() in //example/child:config transitively " + |
| "depends on it.", |
| e.getHumanReadableErrorMessage().replace("\\", "/")); |
| } |
| } |
| } |