blob: a0ec88fdd910aad24e89026afde3d06dd6e99081 [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.java.intellij;
import static com.facebook.buck.testutil.MoreAsserts.assertListEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import com.facebook.buck.android.AndroidBinary;
import com.facebook.buck.android.AndroidBinaryBuilder;
import com.facebook.buck.android.AndroidLibraryBuilder;
import com.facebook.buck.android.AndroidResourceRuleBuilder;
import com.facebook.buck.android.NdkLibrary;
import com.facebook.buck.android.NdkLibraryBuilder;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.java.FakeJavaPackageFinder;
import com.facebook.buck.java.JavaLibraryBuilder;
import com.facebook.buck.java.JavaLibraryDescription;
import com.facebook.buck.java.JavaPackageFinder;
import com.facebook.buck.java.JavaTestBuilder;
import com.facebook.buck.java.KeystoreBuilder;
import com.facebook.buck.java.PrebuiltJarBuilder;
import com.facebook.buck.java.intellij.Project.SourceFolder;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.InMemoryBuildFileTree;
import com.facebook.buck.rules.ActionGraph;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.ProjectConfig;
import com.facebook.buck.rules.ProjectConfigBuilder;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.TestSourcePath;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.testutil.BuckTestConstant;
import com.facebook.buck.testutil.RuleMap;
import com.fasterxml.jackson.databind.ObjectMapper;
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.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
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.nio.file.Paths;
import java.util.List;
import java.util.Properties;
import javax.annotation.Nullable;
public class ProjectTest {
private static final Path PATH_TO_GUAVA_JAR = Paths.get("third_party/guava/guava-10.0.1.jar");
@SuppressWarnings("PMD.UnusedPrivateField")
private BuildRule guava;
/**
* Creates an ActionGraph 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 ActionGraph also includes three project_config rules: one for the android_library, and one
* for each of the android_binary rules.
*/
public ProjectWithModules createActionGraphForTesting(
@Nullable JavaPackageFinder javaPackageFinder) throws IOException {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
// java_library //buck-out/android/com/facebook:R
BuildRule rRule = JavaLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//buck-out/android/com/facebook:R"))
.addSrc(Paths.get("buck-out/android/com/facebook/R.java"))
.build(ruleResolver);
// prebuilt_jar //third_party/guava:guava
guava = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/guava:guava"))
.setBinaryJar(PATH_TO_GUAVA_JAR)
.build(ruleResolver);
// android_resouce android_res/base:res
BuildRule androidResRule = ruleResolver.addToIndex(
AndroidResourceRuleBuilder.newBuilder()
.setResolver(new SourcePathResolver(ruleResolver))
.setBuildTarget(BuildTargetFactory.newInstance("//android_res/base:res"))
.setRes(Paths.get("android_res/base/res"))
.setRDotJavaPackage("com.facebook")
.build());
// project_config android_res/base:res
ProjectConfig projectConfigForResource = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//android_res/base:project_config"))
.setSrcRule(androidResRule.getBuildTarget())
.setSrcRoots(ImmutableList.of("res"))
.build(ruleResolver);
// java_library //java/src/com/facebook/grandchild:grandchild
BuildTarget grandchildTarget =
BuildTargetFactory.newInstance("//java/src/com/facebook/grandchild:grandchild");
BuildRule grandchild = JavaLibraryBuilder
.createBuilder(grandchildTarget)
.addSrc(Paths.get("Grandchild.java"))
.build(ruleResolver);
// java_library //java/src/com/facebook/child:child
BuildRule childRule = JavaLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/src/com/facebook/child:child"))
.addSrc(Paths.get("Child.java"))
.addDep(grandchild.getBuildTarget())
.build(ruleResolver);
// java_library //java/src/com/facebook/exportlib:exportlib
BuildRule exportLib = JavaLibraryBuilder
.createBuilder(
BuildTargetFactory.newInstance("//java/src/com/facebook/exportlib:exportlib"))
.addSrc(Paths.get("ExportLib.java"))
.addDep(guava.getBuildTarget())
.addExportedDep(guava.getBuildTarget())
.build(ruleResolver);
// android_library //java/src/com/facebook/base:base
BuildRule baseRule = AndroidLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/src/com/facebook/base:base"))
.addSrc(Paths.get("Base.java"))
.addDep(rRule.getBuildTarget())
.addDep(exportLib.getBuildTarget())
.addDep(childRule.getBuildTarget())
.addDep(androidResRule.getBuildTarget())
.build(ruleResolver);
// project_config //java/src/com/facebook/base:project_config
ProjectConfig projectConfigForLibrary = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance(
"//java/src/com/facebook/base:project_config"))
.setSrcRule(baseRule.getBuildTarget())
.setSrcRoots(ImmutableList.of("src", "src-gen"))
.build(ruleResolver);
ProjectConfig projectConfigForExportLibrary = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//java/src/com/facebook/exportlib:project_config"))
.setSrcRule(exportLib.getBuildTarget())
.setSrcRoots(ImmutableList.of("src")).build(ruleResolver);
// keystore //keystore:debug
BuildTarget keystoreTarget = BuildTargetFactory.newInstance("//keystore:debug");
BuildRule keystore = KeystoreBuilder.createBuilder(keystoreTarget)
.setStore(Paths.get("keystore/debug.keystore"))
.setProperties(Paths.get("keystore/debug.keystore.properties"))
.build(ruleResolver);
// android_binary //foo:app
ImmutableSortedSet<BuildTarget> androidBinaryRuleDepsTarget =
ImmutableSortedSet.of(baseRule.getBuildTarget());
AndroidBinary androidBinaryRule = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//foo:app"))
.setOriginalDeps(androidBinaryRuleDepsTarget)
.setManifest(new TestSourcePath("foo/AndroidManifest.xml"))
.setTarget("Google Inc.:Google APIs:16")
.setKeystore(keystore.getBuildTarget())
.setBuildTargetsToExcludeFromDex(
ImmutableSet.of(
BuildTargetFactory.newInstance("//third_party/guava:guava")))
.build(ruleResolver);
// project_config //foo:project_config
ProjectConfig projectConfigUsingNoDx = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(BuildTargetFactory.newInstance("//foo:project_config"))
.setSrcRule(androidBinaryRule.getBuildTarget())
.build(ruleResolver);
// android_binary //bar:app
ImmutableSortedSet<BuildTarget> barAppBuildRuleDepsTarget =
ImmutableSortedSet.of(baseRule.getBuildTarget());
AndroidBinary barAppBuildRule = (AndroidBinary) AndroidBinaryBuilder.createBuilder(
BuildTargetFactory.newInstance("//bar:app"))
.setOriginalDeps(barAppBuildRuleDepsTarget)
.setManifest(new TestSourcePath("foo/AndroidManifest.xml"))
.setTarget("Google Inc.:Google APIs:16")
.setKeystore(keystore.getBuildTarget())
.build(ruleResolver);
// project_config //bar:project_config
ProjectConfig projectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(BuildTargetFactory.newInstance("//bar:project_config"))
.setSrcRule(barAppBuildRule.getBuildTarget())
.build(ruleResolver);
return getModulesForActionGraph(
ruleResolver,
ImmutableSet.of(
projectConfigForExportLibrary,
projectConfigForLibrary,
projectConfigForResource,
projectConfigUsingNoDx,
projectConfig),
javaPackageFinder);
}
@Test
public void testGenerateRelativeGenPath() {
String basePathOfModuleWithSlash = "android_res/com/facebook/gifts/";
Path expectedRelativePathToGen =
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 = createActionGraphForTesting(javaPackageFinder);
Project project = projectWithModules.project;
ActionGraph actionGraph = project.getActionGraph();
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", actionGraph),
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", actionGraph),
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", actionGraph), 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", actionGraph), 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", actionGraph), 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", actionGraph);
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',
// )
BuildRule cglib = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/java/easymock:cglib"))
.setBinaryJar(Paths.get("third_party/java/easymock/cglib.jar"))
.build(ruleResolver);
BuildRule objenesis = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/java/easymock:objenesis"))
.setBinaryJar(Paths.get("third_party/java/easymock/objenesis.jar"))
.build(ruleResolver);
BuildRule easymock = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/java/easymock:easymock"))
.setBinaryJar(Paths.get("third_party/java/easymock/easymock.jar"))
.addDep(cglib.getBuildTarget())
.addDep(objenesis.getBuildTarget())
.build(ruleResolver);
BuildTarget easyMockExampleTarget = BuildTargetFactory.newInstance(
"//third_party/java/easymock:example");
BuildRule mockRule = AndroidLibraryBuilder.createBuilder(easyMockExampleTarget)
.addDep(easymock.getBuildTarget())
.build(ruleResolver);
ProjectConfig projectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//third_party/java/easymock:project_config"))
.setSrcRule(mockRule.getBuildTarget())
.build(ruleResolver);
ProjectWithModules projectWithModules = getModulesForActionGraph(
ruleResolver,
ImmutableSet.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.
BuildRule guava = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/java/guava:guava"))
.setBinaryJar(Paths.get("third_party/java/guava.jar"))
.build(ruleResolver);
BuildRule baseBuildRule = JavaLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/com/example/base:base"))
.addDep(guava.getBuildTarget())
.build(ruleResolver);
BuildRule testBuildRule = JavaTestBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/com/example/base:tests"))
.addDep(guava.getBuildTarget())
.build(ruleResolver);
ProjectConfig projectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//java/com/example/base:project_config"))
.setSrcRule(baseBuildRule.getBuildTarget())
.setTestRule(testBuildRule.getBuildTarget())
.setTestRoots(ImmutableList.of("tests"))
.build(ruleResolver);
ProjectWithModules projectWithModules = getModulesForActionGraph(
ruleResolver,
ImmutableSet.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 &lt;orderEntry scope="TEST"/> or an
* &lt;orderEntry scope="COMPILE"/> group.
*/
@Test
public void testThatJarsAreListedBeforeModules() throws IOException {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
BuildRule supportV4 = JavaLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/com/android/support/v4:v4"))
.build(ruleResolver);
BuildRule httpCore = PrebuiltJarBuilder
.createBuilder(BuildTargetFactory.newInstance("//third_party/java/httpcore:httpcore"))
.setBinaryJar(Paths.get("httpcore-4.0.1.jar"))
.build(ruleResolver);
// 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");
BuildRule robolectricRule = JavaLibraryBuilder
.createBuilder(robolectricTarget)
.addDep(supportV4.getBuildTarget())
.addDep(httpCore.getBuildTarget())
.build(ruleResolver);
ProjectConfig projectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//third_party/java/robolectric:project_config"))
.setSrcRule(robolectricRule.getBuildTarget())
.setSrcRoots(ImmutableList.of("src/main/java"))
.build(ruleResolver);
ProjectWithModules projectWithModules = getModulesForActionGraph(
ruleResolver,
ImmutableSet.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();
BuildRule resBuildRule = ruleResolver1.addToIndex(
AndroidResourceRuleBuilder.newBuilder()
.setResolver(new SourcePathResolver(ruleResolver1))
.setBuildTarget(BuildTargetFactory.newInstance("//resources/com/example:res"))
.build());
ProjectConfig projectConfigNullSrcRoots = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//resources/com/example:project_config"))
.setSrcRule(resBuildRule.getBuildTarget())
.setSrcRoots(null)
.build(ruleResolver1);
ProjectWithModules projectWithModules1 = getModulesForActionGraph(
ruleResolver1,
ImmutableSet.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();
BuildRule baseBuildRule = AndroidLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/com/example/base:base"))
.build(ruleResolver2);
ProjectConfig inPackageProjectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//java/com/example/base:project_config"))
.setSrcRule(baseBuildRule.getBuildTarget())
.setSrcRoots(ImmutableList.<String>of())
.build(ruleResolver2);
// 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 = getModulesForActionGraph(
ruleResolver2,
ImmutableSet.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();
BuildRule baseBuildRule3 = AndroidLibraryBuilder
.createBuilder(BuildTargetFactory.newInstance("//java/com/example/base:base"))
.build(ruleResolver3);
ProjectConfig hasSrcFolderProjectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance("//java/com/example/base:project_config"))
.setSrcRule(baseBuildRule3.getBuildTarget())
.setSrcRoots(ImmutableList.of("src"))
.build(ruleResolver3);
ProjectWithModules projectWithModules3 = getModulesForActionGraph(
ruleResolver3,
ImmutableSet.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 getModulesForActionGraph(
BuildRuleResolver ruleResolver,
ImmutableSet<ProjectConfig> projectConfigs,
@Nullable JavaPackageFinder javaPackageFinder) throws IOException {
if (javaPackageFinder == null) {
javaPackageFinder = new FakeJavaPackageFinder();
}
ActionGraph actionGraph = RuleMap.createGraphFromBuildRules(ruleResolver);
// 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(
Paths.get("keystore/debug.keystore.properties")))
.andReturn(keystoreProperties).anyTimes();
ImmutableMap<Path, String> basePathToAliasMap = ImmutableMap.of();
Project project = new Project(
new SourcePathResolver(ruleResolver),
projectConfigs,
actionGraph,
basePathToAliasMap,
javaPackageFinder,
executionContext,
new InMemoryBuildFileTree(
Iterables.transform(
actionGraph.getNodes(),
BuildTarget.TO_TARGET)),
projectFilesystem,
/* pathToDefaultAndroidManifest */ Optional.<String>absent(),
/* pathToPostProcessScript */ Optional.<String>absent(),
BuckTestConstant.PYTHON_INTERPRETER,
new ObjectMapper(),
false);
// 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, ActionGraph actionGraph) {
String[] parts = id.split(":");
BuildRule rule = actionGraph.findBuildRuleByTarget(
BuildTarget.builder(parts[0], parts[1]).build());
Preconditions.checkNotNull(rule, "No rule for %s", id);
return rule;
}
@Test
public void testNdkLibraryHasCorrectPath() throws IOException {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver);
// 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");
NdkLibrary ndkLibrary = NdkLibraryBuilder.createNdkLibrary(fooJni, pathResolver)
.addSrc(Paths.get("Android.mk"))
.build();
ruleResolver.addToIndex(ndkLibrary);
ProjectConfig ndkProjectConfig = (ProjectConfig) ProjectConfigBuilder
.newProjectConfigRuleBuilder(
BuildTargetFactory.newInstance(
"//third_party/java/foo/jni:project_config"))
.setSrcRule(ndkLibrary.getBuildTarget())
.build(ruleResolver);
ProjectWithModules projectWithModules = getModulesForActionGraph(
ruleResolver,
ImmutableSet.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.getLibraryPath()),
androidLibraryModule.nativeLibs);
}
@Test
public void testDoNotIgnoreAllOfBuckOut() {
SourcePathResolver resolver = new SourcePathResolver(new BuildRuleResolver());
ProjectFilesystem projectFilesystem = EasyMock.createMock(ProjectFilesystem.class);
ImmutableSet<Path> ignorePaths = ImmutableSet.of(Paths.get("buck-out"), Paths.get(".git"));
EasyMock.expect(projectFilesystem.getIgnorePaths()).andReturn(ignorePaths);
EasyMock.replay(projectFilesystem);
BuildTarget buildTarget = BuildTarget.builder("//", "base").build();
BuildRule buildRule = new FakeBuildRule(JavaLibraryDescription.TYPE, buildTarget, resolver);
Module module = new Module(buildRule, buildTarget);
Project.addRootExcludes(module, buildRule, projectFilesystem);
ImmutableSortedSet<SourceFolder> expectedExcludeFolders =
ImmutableSortedSet.orderedBy(Module.ALPHABETIZER)
.add(new SourceFolder("file://$MODULE_DIR$/.git", /* isTestSource */ false))
.add(new SourceFolder("file://$MODULE_DIR$/buck-out/bin", /* isTestSource */ false))
.add(new SourceFolder("file://$MODULE_DIR$/buck-out/log", /* isTestSource */ false))
.build();
assertEquals("Specific subfolders of buck-out should be excluded rather than all of buck-out.",
expectedExcludeFolders,
module.excludeFolders);
EasyMock.verify(projectFilesystem);
}
}