blob: cd6326dc9e9e1db4d497475f7fe253c45e6f5086 [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.rules;
import static com.facebook.buck.util.BuckConstant.BIN_DIR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.android.AndroidLibraryRule;
import com.facebook.buck.java.DependencyCheckingJavacStep;
import com.facebook.buck.java.JavacInMemoryStep;
import com.facebook.buck.java.JavacOptionsUtil;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.BuildTargetPattern;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.Verbosity;
import com.facebook.buck.step.fs.MkdirAndSymlinkFileStep;
import com.facebook.buck.testutil.MoreAsserts;
import com.facebook.buck.testutil.RuleMap;
import com.facebook.buck.util.BuckConstant;
import com.facebook.buck.util.DefaultDirectoryTraverser;
import com.facebook.buck.util.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import org.easymock.EasyMock;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
public class DefaultJavaLibraryRuleTest {
private static final String ANNOTATION_SCENARIO_TARGET =
"//android/java/src/com/facebook:fb";
private static final String ANNOTATION_SCENARIO_GEN_PATH =
BuckConstant.ANNOTATION_DIR + "/android/java/src/com/facebook/__fb_gen__";
@Test
public void testAddResourceCommandsWithBuildFileParentOfSrcDirectory() {
// Files:
// android/java/BUILD
// android/java/src/com/facebook/base/data.json
// android/java/src/com/facebook/common/util/data.json
BuildTarget buildTarget = BuildTargetFactory.newInstance(
"//android/java", "resources", new File("android/java/BUILD"));
ImmutableSortedSet<BuildRule> deps = ImmutableSortedSet.of();
ImmutableSet<BuildTargetPattern> visibilityPatterns = ImmutableSet.of();
DefaultJavaLibraryRule javaRule = new DefaultJavaLibraryRule(
new BuildRuleParams(buildTarget, deps, visibilityPatterns),
ImmutableSet.<String>of() /* srcs */,
ImmutableSet.of(
"android/java/src/com/facebook/base/data.json",
"android/java/src/com/facebook/common/util/data.json"),
null,
AnnotationProcessingParams.EMPTY,
/* exportDeps */ false);
ImmutableList.Builder<Step> commands = ImmutableList.builder();
JavaPackageFinder javaPackageFinder = createJavaPackageFinder();
javaRule.addResourceCommands(
commands, BIN_DIR + "/android/java/lib__resources__classes", javaPackageFinder);
List<? extends Step> expected = ImmutableList.of(
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/base/data.json",
BIN_DIR + "/android/java/lib__resources__classes/com/facebook/base/data.json"),
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/common/util/data.json",
BIN_DIR + "/android/java/lib__resources__classes/com/facebook/common/util/data.json"));
MoreAsserts.assertListEquals(expected, commands.build());
EasyMock.verify(javaPackageFinder);
}
@Test
public void testAddResourceCommandsWithBuildFileParentOfJavaPackage() {
// Files:
// android/java/src/BUILD
// android/java/src/com/facebook/base/data.json
// android/java/src/com/facebook/common/util/data.json
BuildTarget buildTarget = BuildTargetFactory.newInstance(
"//android/java/src", "resources", new File("android/java/src/BUILD"));
ImmutableSortedSet<BuildRule> deps = ImmutableSortedSet.of();
ImmutableSet<BuildTargetPattern> visibilityPatterns = ImmutableSet.of();
DefaultJavaLibraryRule javaRule = new DefaultJavaLibraryRule(
new BuildRuleParams(buildTarget, deps, visibilityPatterns),
ImmutableSet.<String>of() /* srcs */,
ImmutableSet.of(
"android/java/src/com/facebook/base/data.json",
"android/java/src/com/facebook/common/util/data.json"),
/* proguargConfig */ null,
AnnotationProcessingParams.EMPTY,
/* exportDeps */ false);
ImmutableList.Builder<Step> commands = ImmutableList.builder();
JavaPackageFinder javaPackageFinder = createJavaPackageFinder();
javaRule.addResourceCommands(
commands, BIN_DIR + "/android/java/src/lib__resources__classes", javaPackageFinder);
List<? extends Step> expected = ImmutableList.of(
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/base/data.json",
BIN_DIR + "/android/java/src/lib__resources__classes/com/facebook/base/data.json"),
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/common/util/data.json",
BIN_DIR + "/android/java/src/lib__resources__classes/com/facebook/common/util/data.json"));
MoreAsserts.assertListEquals(expected, commands.build());
EasyMock.verify(javaPackageFinder);
}
@Test
public void testAddResourceCommandsWithBuildFileInJavaPackage() {
// Files:
// android/java/src/com/facebook/BUILD
// android/java/src/com/facebook/base/data.json
// android/java/src/com/facebook/common/util/data.json
BuildTarget buildTarget = BuildTargetFactory.newInstance(
"//android/java/src/com/facebook",
"resources",
new File("android/java/src/com/facebook/BUILD"));
ImmutableSortedSet<BuildRule> deps = ImmutableSortedSet.of();
ImmutableSet<BuildTargetPattern> visibilityPatterns = ImmutableSet.of();
DefaultJavaLibraryRule javaRule = new DefaultJavaLibraryRule(
new BuildRuleParams(buildTarget, deps, visibilityPatterns),
ImmutableSet.<String>of() /* srcs */,
ImmutableSet.of(
"android/java/src/com/facebook/base/data.json",
"android/java/src/com/facebook/common/util/data.json"),
/* proguargConfig */ null,
AnnotationProcessingParams.EMPTY,
/* exportDeps */ false);
ImmutableList.Builder<Step> commands = ImmutableList.builder();
JavaPackageFinder javaPackageFinder = createJavaPackageFinder();
javaRule.addResourceCommands(
commands,
BIN_DIR + "/android/java/src/com/facebook/lib__resources__classes",
javaPackageFinder);
List<? extends Step> expected = ImmutableList.of(
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/base/data.json",
BIN_DIR + "/android/java/src/com/facebook/lib__resources__classes/com/facebook/base/data.json"),
new MkdirAndSymlinkFileStep(
"android/java/src/com/facebook/common/util/data.json",
BIN_DIR + "/android/java/src/com/facebook/lib__resources__classes/com/facebook/common/util/data.json"));
MoreAsserts.assertListEquals(expected, commands.build());
EasyMock.verify(javaPackageFinder);
}
/** Make sure that when isAndroidLibrary is true, that the Android bootclasspath is used. */
@Test
public void testBuildInternalWithAndroidBootclasspath() throws IOException {
BuildTarget buildTarget = BuildTargetFactory.newInstance("//android/java/src/com/facebook:fb");
String src = "android/java/src/com/facebook/Main.java";
DefaultJavaLibraryRule javaLibrary = AndroidLibraryRule.newAndroidLibraryRuleBuilder()
.setBuildTarget(buildTarget)
.addSrc(src)
.build(Maps.<String, BuildRule>newHashMap());
String bootclasspath = "effects.jar:maps.jar:usb.jar:";
BuildContext context = createBuildContext(javaLibrary, bootclasspath);
List<Step> steps = javaLibrary.buildInternal(context);
// Find the JavacInMemoryCommand and verify its bootclasspath.
Step step = Iterables.find(steps, new Predicate<Step>() {
@Override
public boolean apply(Step command) {
return command instanceof JavacInMemoryStep;
}
});
assertNotNull("Expected a JavacInMemoryCommand in the command list.", step);
JavacInMemoryStep javac = (JavacInMemoryStep) step;
assertEquals("Should compile Main.java rather than generated R.java.",
ImmutableSet.of(src),
javac.getSrcs());
EasyMock.verify(context);
}
/**
* Verify that no annotation options are there if we do not add an
* annotation processor.
*/
@Test
public void testNoAnnotationProcessor() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
for (String parameter : parameters) {
assertNotEquals("Expected no -processorpath parameters", parameter, "-processorpath");
assertNotEquals("Expected no -processor parameters", parameter, "-processor");
assertNotEquals("Expected no -s parameters", parameter, "-s");
assertFalse("Expected no annotation options", parameter.startsWith("-A"));
}
}
/**
* Verify adding an annotation processor java binary.
*/
@Test
public void testAddAnnotationProcessorJavaBinary() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_JAVA_BINARY);
scenario.getAnnotationProcessingParamsBuilder()
.addAllProcessors(ImmutableList.of("MyProcessor"));
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
MoreAsserts.assertContainsOne(parameters, "-processorpath");
MoreAsserts.assertContainsOne(parameters, "-processor");
MoreAsserts.assertContainsOne(parameters, "MyProcessor");
MoreAsserts.assertContainsOne(parameters, "-s");
MoreAsserts.assertContainsOne(parameters, ANNOTATION_SCENARIO_GEN_PATH);
assertEquals(
"Expected '-processor MyProcessor' parameters",
parameters.indexOf("-processor") + 1,
parameters.indexOf("MyProcessor"));
assertEquals(
"Expected '-s " + ANNOTATION_SCENARIO_GEN_PATH + "' parameters",
parameters.indexOf("-s") + 1,
parameters.indexOf(ANNOTATION_SCENARIO_GEN_PATH));
for (String parameter : parameters) {
assertFalse("Expected no annotation options", parameter.startsWith("-A"));
}
}
/**
* Verify adding an annotation processor prebuilt jar.
*/
@Test
public void testAddAnnotationProcessorPrebuiltJar() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_PREBUILT_JAR);
scenario.getAnnotationProcessingParamsBuilder()
.addAllProcessors(ImmutableList.of("MyProcessor"));
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
MoreAsserts.assertContainsOne(parameters, "-processorpath");
MoreAsserts.assertContainsOne(parameters, "-processor");
MoreAsserts.assertContainsOne(parameters, "MyProcessor");
MoreAsserts.assertContainsOne(parameters, "-s");
MoreAsserts.assertContainsOne(parameters, ANNOTATION_SCENARIO_GEN_PATH);
}
/**
* Verify adding an annotation processor java library.
*/
@Test
public void testAddAnnotationProcessorJavaLibrary() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_PREBUILT_JAR);
scenario.getAnnotationProcessingParamsBuilder()
.addAllProcessors(ImmutableList.of("MyProcessor"));
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
MoreAsserts.assertContainsOne(parameters, "-processorpath");
MoreAsserts.assertContainsOne(parameters, "-processor");
MoreAsserts.assertContainsOne(parameters, "MyProcessor");
MoreAsserts.assertContainsOne(parameters, "-s");
MoreAsserts.assertContainsOne(parameters, ANNOTATION_SCENARIO_GEN_PATH);
}
/**
* Verify adding multiple annotation processors.
*/
@Test
public void testAddAnnotationProcessorJar() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_PREBUILT_JAR);
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_JAVA_BINARY);
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_JAVA_LIBRARY);
scenario.getAnnotationProcessingParamsBuilder()
.addAllProcessors(ImmutableList.of("MyProcessor"));
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
MoreAsserts.assertContainsOne(parameters, "-processorpath");
MoreAsserts.assertContainsOne(parameters, "-processor");
MoreAsserts.assertContainsOne(parameters, "MyProcessor");
MoreAsserts.assertContainsOne(parameters, "-s");
MoreAsserts.assertContainsOne(parameters, ANNOTATION_SCENARIO_GEN_PATH);
}
@Test
public void testGetClasspathEntriesMap() {
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget libraryOneTarget = BuildTargetFactory.newInstance("//:libone");
JavaLibraryRule libraryOne = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryOneTarget)
.addSrc("java/src/com/libone/Bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(libraryOne.getFullyQualifiedName(), libraryOne);
BuildTarget libraryTwoTarget = BuildTargetFactory.newInstance("//:libtwo");
JavaLibraryRule libraryTwo = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryTwoTarget)
.addSrc("java/src/com/libtwo/Foo.java")
.addDep("//:libone")
.build(buildRuleIndex);
buildRuleIndex.put(libraryTwo.getFullyQualifiedName(), libraryTwo);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
JavaLibraryRule parent = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/Meh.java")
.addDep("//:libtwo")
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
assertEquals(ImmutableSetMultimap.of(
libraryOne, "buck-out/gen/lib__libone__output/libone.jar",
libraryTwo, "buck-out/gen/lib__libtwo__output/libtwo.jar",
parent, "buck-out/gen/lib__parent__output/parent.jar"),
parent.getTransitiveClasspathEntries());
}
/**
* Verify adding an annotation processor java binary with options.
*/
@Test
public void testAddAnnotationProcessorWithOptions() throws IOException {
AnnotationProcessingScenario scenario = new AnnotationProcessingScenario();
scenario.addAnnotationProcessorTarget(AnnotationProcessorTarget.VALID_JAVA_BINARY);
scenario.getAnnotationProcessingParamsBuilder().addAllProcessors(ImmutableList.of("MyProcessor"));
scenario.getAnnotationProcessingParamsBuilder().addParameter("MyParameter");
scenario.getAnnotationProcessingParamsBuilder().addParameter("MyKey=MyValue");
scenario.getAnnotationProcessingParamsBuilder().setProcessOnly(true);
ImmutableList<String> parameters = scenario.buildAndGetCompileParameters();
MoreAsserts.assertContainsOne(parameters, "-processorpath");
MoreAsserts.assertContainsOne(parameters, "-processor");
MoreAsserts.assertContainsOne(parameters, "MyProcessor");
MoreAsserts.assertContainsOne(parameters, "-s");
MoreAsserts.assertContainsOne(parameters, ANNOTATION_SCENARIO_GEN_PATH);
MoreAsserts.assertContainsOne(parameters, "-proc:only");
assertEquals(
"Expected '-processor MyProcessor' parameters",
parameters.indexOf("-processor") + 1,
parameters.indexOf("MyProcessor"));
assertEquals(
"Expected '-s " + ANNOTATION_SCENARIO_GEN_PATH + "' parameters",
parameters.indexOf("-s") + 1,
parameters.indexOf(ANNOTATION_SCENARIO_GEN_PATH));
MoreAsserts.assertContainsOne(parameters, "-AMyParameter");
MoreAsserts.assertContainsOne(parameters, "-AMyKey=MyValue");
}
@Test
public void testExportDeps() {
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget libraryOneTarget = BuildTargetFactory.newInstance("//:libone");
JavaLibraryRule libraryOne = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryOneTarget)
.addSrc("java/src/com/libone/Bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(libraryOne.getFullyQualifiedName(), libraryOne);
BuildTarget libraryTwoTarget = BuildTargetFactory.newInstance("//:libtwo");
JavaLibraryRule libraryTwo = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryTwoTarget)
.addSrc("java/src/com/libtwo/Foo.java")
.addDep("//:libone")
.setExportDeps(true)
.build(buildRuleIndex);
buildRuleIndex.put(libraryTwo.getFullyQualifiedName(), libraryTwo);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
JavaLibraryRule parent = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/Meh.java")
.addDep("//:libtwo")
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
assertEquals(ImmutableSet.of("buck-out/gen/lib__libone__output/libone.jar"),
libraryOne.getOutputClasspathEntries());
assertEquals(
ImmutableSet.of("buck-out/gen/lib__libone__output/libone.jar",
"buck-out/gen/lib__libtwo__output/libtwo.jar"),
libraryTwo.getOutputClasspathEntries());
ImmutableSetMultimap.Builder<BuildRule, String> expected = ImmutableSetMultimap.builder();
expected.put(parent, "buck-out/gen/lib__parent__output/parent.jar");
expected.putAll(libraryTwo,
"buck-out/gen/lib__libone__output/libone.jar",
"buck-out/gen/lib__libtwo__output/libtwo.jar");
assertEquals(expected.build(), parent.getDeclaredClasspathEntries());
}
@Test
public void testEmptySuggestBuildFunction() {
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget libraryOneTarget = BuildTargetFactory.newInstance("//:libone");
DefaultJavaLibraryRule libraryOne = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryOneTarget)
.addSrc("java/src/com/libone/bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(libraryOne.getFullyQualifiedName(), libraryOne);
BuildContext context = createSuggestContext(buildRuleIndex,
BuildDependencies.FIRST_ORDER_ONLY);
ImmutableSetMultimap<BuildRule, String> classpathEntries =
libraryOne.getTransitiveClasspathEntries();
assertEquals(
Optional.<DependencyCheckingJavacStep.SuggestBuildRules>absent(),
libraryOne.createSuggestBuildFunction(context,
classpathEntries,
classpathEntries,
createJarResolver(/* classToSymbols */ImmutableMap.<String, String>of())));
EasyMock.verify(context);
}
@Test
public void testSuggsetDepsReverseTopoSortRespected() {
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget libraryOneTarget = BuildTargetFactory.newInstance("//:libone");
DefaultJavaLibraryRule libraryOne = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryOneTarget)
.addSrc("java/src/com/libone/Bar.java")
.addVisibilityPattern(BuildTargetPattern.MATCH_ALL)
.build(buildRuleIndex);
buildRuleIndex.put(libraryOne.getFullyQualifiedName(), libraryOne);
BuildTarget libraryTwoTarget = BuildTargetFactory.newInstance("//:libtwo");
DefaultJavaLibraryRule libraryTwo = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(libraryTwoTarget)
.addSrc("java/src/com/libtwo/Foo.java")
.addDep("//:libone")
.build(buildRuleIndex);
buildRuleIndex.put(libraryTwo.getFullyQualifiedName(), libraryTwo);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
DefaultJavaLibraryRule parent = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/Meh.java")
.addDep("//:libtwo")
.addVisibilityPattern(BuildTargetPattern.MATCH_ALL)
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
BuildTarget grandparentTarget = BuildTargetFactory.newInstance("//:grandparent");
DefaultJavaLibraryRule grandparent = DefaultJavaLibraryRule.newJavaLibraryRuleBuilder()
.setBuildTarget(grandparentTarget)
.addSrc("java/src/com/parent/OldManRiver.java")
.addDep("//:parent")
.build(buildRuleIndex);
buildRuleIndex.put(grandparent.getFullyQualifiedName(), parent);
BuildContext context = createSuggestContext(buildRuleIndex,
BuildDependencies.WARN_ON_TRANSITIVE);
ImmutableSetMultimap<BuildRule, String> transitive =
parent.getTransitiveClasspathEntries();
ImmutableMap<String, String> classToSymbols = ImmutableMap.of(
Iterables.getFirst(transitive.get(parent), null), "com.facebook.Foo",
Iterables.getFirst(transitive.get(libraryOne), null), "com.facebook.Bar",
Iterables.getFirst(transitive.get(libraryTwo), null), "com.facebook.Foo");
Optional<DependencyCheckingJavacStep.SuggestBuildRules> suggestFn =
grandparent.createSuggestBuildFunction(context,
transitive,
/* declaredClasspathEntries */ ImmutableSetMultimap.<BuildRule, String>of(),
createJarResolver(classToSymbols));
assertTrue(suggestFn.isPresent());
assertEquals(ImmutableSet.of("//:parent", "//:libone"),
suggestFn.get().apply(ImmutableSet.of("com.facebook.Foo", "com.facebook.Bar")));
EasyMock.verify(context);
}
@Test
public void testTransitiveDepsCached() throws IOException {
// If building with anything but FIRST_ORDER_ONLY, then fallback to the default caching
// algorithm.
BuildContext context = EasyMock.createMock(BuildContext.class);
EasyMock.expect(context.getBuildDependencies()).andReturn(BuildDependencies.WARN_ON_TRANSITIVE);
EasyMock.replay(context);
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget grandChildTarget = BuildTargetFactory.newInstance("//:grandChild");
JavaLibraryRule grandChild = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(false)
.setRuleInputsAreCached(false)
.setBuildTarget(grandChildTarget)
.addSrc("java/src/com/grandchild/bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(grandChild.getFullyQualifiedName(), grandChild);
BuildTarget childTarget = BuildTargetFactory.newInstance("//:child");
JavaLibraryRule child = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setBuildTarget(childTarget)
.addSrc("java/src/com/child/foo.java")
.addDep("//:grandChild")
.build(buildRuleIndex);
buildRuleIndex.put(child.getFullyQualifiedName(), child);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
DefaultJavaLibraryRule parent = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/foo.java")
.addDep("//:child")
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
Logger logger = EasyMock.createMock(Logger.class);
logger.info("//:parent not cached because //:child has an uncached descendant");
EasyMock.replay(logger);
assertFalse(parent.depsCached(context, logger));
EasyMock.verify(context, logger);
}
@Test
public void testDepsCached() throws IOException {
// If building with FIRST_ORDER_ONLY, only rebuild if we really need it.
BuildContext context = EasyMock.createMock(BuildContext.class);
EasyMock.expect(context.getBuildDependencies()).andReturn(BuildDependencies.FIRST_ORDER_ONLY);
EasyMock.replay(context);
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget grandChildTarget = BuildTargetFactory.newInstance("//:grandChild");
JavaLibraryRule grandChild = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(false)
.setRuleInputsAreCached(false)
.setBuildTarget(grandChildTarget)
.addSrc("java/src/com/grandchild/bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(grandChild.getFullyQualifiedName(), grandChild);
BuildTarget childTarget = BuildTargetFactory.newInstance("//:child");
JavaLibraryRule child = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setBuildTarget(childTarget)
.addSrc("java/src/com/child/foo.java")
.addDep("//:grandChild")
.build(buildRuleIndex);
buildRuleIndex.put(child.getFullyQualifiedName(), child);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
DefaultJavaLibraryRule parent = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/foo.java")
.addDep("//:child")
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
Logger logger = EasyMock.createMock(Logger.class);
EasyMock.replay(logger);
assertTrue(parent.depsCached(context, logger));
EasyMock.verify(context, logger);
}
@Test
public void testExportDepsCached() throws IOException {
// If building with FIRST_ORDER_ONLY, only rebuild if we really need it.
BuildContext context = EasyMock.createMock(BuildContext.class);
EasyMock.expect(context.getBuildDependencies()).andReturn(BuildDependencies.FIRST_ORDER_ONLY);
EasyMock.replay(context);
Map<String, BuildRule> buildRuleIndex = Maps.newHashMap();
BuildTarget grandChildTarget = BuildTargetFactory.newInstance("//:grandChild");
JavaLibraryRule grandChild = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(false)
.setRuleInputsAreCached(false)
.setBuildTarget(grandChildTarget)
.addSrc("java/src/com/grandchild/bar.java")
.build(buildRuleIndex);
buildRuleIndex.put(grandChild.getFullyQualifiedName(), grandChild);
BuildTarget childTarget = BuildTargetFactory.newInstance("//:child");
JavaLibraryRule child = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setExportDeps(true)
.setBuildTarget(childTarget)
.addSrc("java/src/com/child/foo.java")
.addDep("//:grandChild")
.build(buildRuleIndex);
buildRuleIndex.put(child.getFullyQualifiedName(), child);
BuildTarget parentTarget = BuildTargetFactory.newInstance("//:parent");
DefaultJavaLibraryRule parent = FakeDefaultJavaLibraryRule.newFakeJavaLibraryRuleBuilder()
.setHasUncachedDescendants(true)
.setIsCached(true)
.setRuleInputsAreCached(true)
.setBuildTarget(parentTarget)
.addSrc("java/src/com/parent/foo.java")
.addDep("//:child")
.build(buildRuleIndex);
buildRuleIndex.put(parent.getFullyQualifiedName(), parent);
Logger logger = EasyMock.createMock(Logger.class);
logger.info("//:parent not cached because java library //:child exports its deps and has " +
"uncached descendants");
EasyMock.replay(logger);
assertFalse(parent.depsCached(context, logger));
EasyMock.verify(context, logger);
}
// Utilities
private DefaultJavaLibraryRule.JarResolver createJarResolver(
final ImmutableMap<String, String> classToSymbols) {
ImmutableSetMultimap.Builder<String, String> resolveMapBuilder =
ImmutableSetMultimap.builder();
for (Map.Entry<String, String> entry : classToSymbols.entrySet()) {
String fullyQualified = entry.getValue();
String packageName = fullyQualified.substring(0, fullyQualified.lastIndexOf('.'));
String className = fullyQualified.substring(fullyQualified.lastIndexOf('.'));
resolveMapBuilder.putAll(entry.getKey(), fullyQualified, packageName, className);
}
final ImmutableSetMultimap<String, String> resolveMap = resolveMapBuilder.build();
return new DefaultJavaLibraryRule.JarResolver() {
@Override
public ImmutableSet<String> apply(String input) {
if (resolveMap.containsKey(input)) {
return resolveMap.get(input);
} else {
return ImmutableSet.of();
}
}
};
}
private JavaPackageFinder createJavaPackageFinder() {
JavaPackageFinder javaPackageFinder = EasyMock.createMock(JavaPackageFinder.class);
EasyMock.expect(javaPackageFinder.findJavaPackageFolderForPath(
"android/java/src/com/facebook/base/data.json"))
.andReturn("com/facebook/base/");
EasyMock.expect(javaPackageFinder.findJavaPackageFolderForPath(
"android/java/src/com/facebook/common/util/data.json"))
.andReturn("com/facebook/common/util/");
EasyMock.replay(javaPackageFinder);
return javaPackageFinder;
}
private BuildContext createSuggestContext(Map<String, BuildRule> buildRuleIndex,
BuildDependencies buildDependencies) {
DependencyGraph graph = RuleMap.createGraphFromBuildRules(
ImmutableMap.copyOf(buildRuleIndex));
BuildContext context = EasyMock.createMock(BuildContext.class);
EasyMock.expect(context.getDependencyGraph()).andReturn(graph);
EasyMock.expectLastCall().anyTimes();
EasyMock.expect(context.getBuildDependencies()).andReturn(buildDependencies).anyTimes();
EasyMock.replay(context);
return context;
}
private BuildContext createBuildContext(DefaultJavaLibraryRule javaLibrary,
String bootclasspath) {
DependencyGraph graph = RuleMap.createGraphFromBuildRules(
ImmutableMap.<String, BuildRule>of(
javaLibrary.getFullyQualifiedName(),
javaLibrary));
BuildContext context = EasyMock.createMock(BuildContext.class);
EasyMock.expect(context.getDependencyGraph()).andReturn(graph);
EasyMock.expectLastCall().anyTimes();
EasyMock.expect(context.getAndroidBootclasspathSupplier()).andReturn(Suppliers.ofInstance(
bootclasspath));
EasyMock.expect(context.getJavaPackageFinder()).andReturn(
EasyMock.createMock(JavaPackageFinder.class));
EasyMock.expect(context.getBuildDependencies()).andReturn(BuildDependencies.TRANSITIVE);
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(context);
return context;
}
private enum AnnotationProcessorTarget {
INVALID("//tools/java/src/com/facebook/somejava:generator") {
@Override
public BuildRule createRule(BuildTarget target) {
return new Genrule(
createBuildRuleParams(target),
ImmutableList.of("MyInput"),
"echo hi",
"MyOutput",
Functions.RELATIVE_TO_ABSOLUTE_PATH);
}
},
VALID_PREBUILT_JAR("//tools/java/src/com/someone/library:prebuilt-processors") {
@Override
public BuildRule createRule(BuildTarget target) {
return new PrebuiltJarRule(
createBuildRuleParams(target),
"MyJar",
null,
null);
}
},
VALID_JAVA_BINARY("//tools/java/src/com/facebook/annotations:custom-processors") {
@Override
public BuildRule createRule(BuildTarget target) {
return new JavaBinaryRule(
createBuildRuleParams(target),
"com.facebook.Main",
null,
null,
new DefaultDirectoryTraverser());
}
},
VALID_JAVA_LIBRARY("//tools/java/src/com/facebook/somejava:library") {
@Override
public BuildRule createRule(BuildTarget target) {
return new DefaultJavaLibraryRule(
createBuildRuleParams(target),
ImmutableSet.<String>of("MyClass.java"),
ImmutableSet.<String>of(),
"MyProguardConfig",
AnnotationProcessingParams.EMPTY,
/* exportDeps */ false);
}
};
private final String targetName;
private AnnotationProcessorTarget(String targetName) {
this.targetName = targetName;
}
protected BuildRuleParams createBuildRuleParams(BuildTarget target) {
return new BuildRuleParams(
target,
ImmutableSortedSet.<BuildRule>of(),
ImmutableSet.of(BuildTargetPattern.MATCH_ALL));
}
public BuildTarget createTarget() {
return BuildTargetFactory.newInstance(targetName);
}
public abstract BuildRule createRule(BuildTarget target);
}
// Captures all the common code between the different annotation processing test scenarios.
private class AnnotationProcessingScenario {
private final Map<String,BuildRule> buildRuleIndex;
private final AnnotationProcessingParams.Builder annotationProcessingParamsBuilder;
private ExecutionContext executionContext;
private BuildContext buildContext;
public AnnotationProcessingScenario() {
annotationProcessingParamsBuilder = new AnnotationProcessingParams.Builder();
buildRuleIndex = Maps.newHashMap();
}
public AnnotationProcessingParams.Builder getAnnotationProcessingParamsBuilder() {
return annotationProcessingParamsBuilder;
}
public void addAnnotationProcessorTarget(AnnotationProcessorTarget processor) {
BuildTarget target = processor.createTarget();
BuildRule rule = processor.createRule(target);
annotationProcessingParamsBuilder.addProcessorBuildTarget(target);
buildRuleIndex.put(target.getFullyQualifiedName(), rule);
}
public ImmutableList<String> buildAndGetCompileParameters() throws IOException {
DefaultJavaLibraryRule javaLibrary = createJavaLibraryRule();
buildContext = createBuildContext(javaLibrary, "");
List<Step> steps = javaLibrary.buildInternal(buildContext);
JavacInMemoryStep javacCommand = lastJavacCommand(steps);
executionContext = EasyMock.createMock(ExecutionContext.class);
EasyMock.expect(executionContext.getVerbosity()).andReturn(Verbosity.SILENT);
EasyMock.replay(executionContext);
ImmutableList<String> options = javacCommand.getOptions(executionContext,
/* buildClasspathEntries */ ImmutableSet.<String>of());
EasyMock.verify(buildContext, executionContext);
return options;
}
// TODO(simons): Actually generate a java library rule, rather than an android one.
private DefaultJavaLibraryRule createJavaLibraryRule() {
BuildTarget buildTarget = BuildTargetFactory.newInstance(ANNOTATION_SCENARIO_TARGET);
annotationProcessingParamsBuilder.setOwnerTarget(buildTarget);
String src = "android/java/src/com/facebook/Main.java";
return new AndroidLibraryRule(
new BuildRuleParams(
buildTarget,
/* deps */ ImmutableSortedSet.<BuildRule>of(),
/* visibilityPatterns */ ImmutableSet.<BuildTargetPattern>of()
),
ImmutableSet.of(src),
/* resources */ ImmutableSet.<String>of(),
/* proguardConfig */ null,
/* annotationProcessors */ annotationProcessingParamsBuilder.build(buildRuleIndex),
/* manifestFile */ null,
JavacOptionsUtil.DEFAULT_SOURCE_LEVEL,
JavacOptionsUtil.DEFAULT_TARGET_LEVEL);
}
private JavacInMemoryStep lastJavacCommand(Iterable<Step> commands) {
Step javac = null;
for (Step step : commands) {
if (step instanceof JavacInMemoryStep) {
javac = step;
// Intentionally no break here, since we want the last one.
}
}
assertNotNull("Expected a JavacInMemoryCommand in command list", javac);
return (JavacInMemoryStep)javac;
}
}
}