blob: 78e1e615237c1c499731db6119c990cb1a8ffeaa [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.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 &lt;orderEntry scope="TEST"/> or an
* &lt;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("\\", "/"));
}
}
}