Fix WARN_ON_TRANSITIVE.
Summary:
The WARN_ON_TRANSITIVE flag seems to have been broken for a while.
Fix it and add tests.
Test Plan: tests
diff --git a/build.xml b/build.xml
index 1ca91f3..aa76119 100644
--- a/build.xml
+++ b/build.xml
@@ -243,6 +243,7 @@
</fileset>
<fileset dir="${test.dir}">
<include name="**/*.java"/>
+ <exclude name="**/testdata/**"/>
</fileset>
<fileset dir="${plugin.dir}">
<include name="**/*.java"/>
diff --git a/src/com/facebook/buck/java/DefaultJavaLibraryRule.java b/src/com/facebook/buck/java/DefaultJavaLibraryRule.java
index 1e45507..ab560be 100644
--- a/src/com/facebook/buck/java/DefaultJavaLibraryRule.java
+++ b/src/com/facebook/buck/java/DefaultJavaLibraryRule.java
@@ -53,8 +53,8 @@
import com.facebook.buck.step.fs.MkdirAndSymlinkFileStep;
import com.facebook.buck.util.BuckConstant;
import com.facebook.buck.util.MorePaths;
+import com.facebook.buck.util.ProjectFilesystem;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
@@ -145,16 +145,19 @@
* jar.
*/
@VisibleForTesting
- static interface JarResolver extends Function<String, ImmutableSet<String>> {}
+ static interface JarResolver {
+ public ImmutableSet<String> resolve(ProjectFilesystem filesystem, Path relativeClassPath);
+ }
private final JarResolver JAR_RESOLVER =
new JarResolver() {
@Override
- public ImmutableSet<String> apply(String classPath) {
+ public ImmutableSet<String> resolve(ProjectFilesystem filesystem, Path relativeClassPath) {
ImmutableSet.Builder<String> topLevelSymbolsBuilder = ImmutableSet.builder();
try {
+ Path classPath = filesystem.getFileForRelativePath(relativeClassPath).toPath();
ClassLoader loader = URLClassLoader.newInstance(
- new URL[]{new File(classPath).toURI().toURL()},
+ new URL[]{classPath.toUri().toURL()},
/* parent */ null);
// For every class contained in that jar, check to see if the package name
@@ -621,7 +624,8 @@
* @return whether or not adding {@code transitiveNotDeclaredDep} as a dependency to this build
* rule would have satisfied one of the {@code failedImports}.
*/
- private boolean isMissingBuildRule(BuildRule transitiveNotDeclaredDep,
+ private boolean isMissingBuildRule(ProjectFilesystem filesystem,
+ BuildRule transitiveNotDeclaredDep,
Set<String> failedImports,
JarResolver jarResolver) {
if (!(transitiveNotDeclaredDep instanceof JavaLibraryRule)) {
@@ -635,8 +639,7 @@
// the exception of rules that export their dependencies, this will result in a single
// classpath.
for (String classPath : classPaths) {
- ImmutableSet<String> topLevelSymbols;
- topLevelSymbols = jarResolver.apply(classPath);
+ ImmutableSet<String> topLevelSymbols = jarResolver.resolve(filesystem, Paths.get(classPath));
for (String symbolName : topLevelSymbols) {
if (failedImports.contains(symbolName)) {
@@ -668,7 +671,7 @@
}
final Set<JavaLibraryRule> transitiveNotDeclaredDeps = Sets.difference(
transitiveClasspathEntries.keySet(),
- declaredClasspathEntries.keySet());
+ Sets.union(ImmutableSet.of(this), declaredClasspathEntries.keySet()));
final ImmutableList<BuildRule> sortedTransitiveNotDeclaredDeps = ImmutableList.copyOf(
TopologicalSort.sort(context.getDependencyGraph(),
@@ -682,7 +685,8 @@
JavacInMemoryStep.SuggestBuildRules suggestBuildRuleFn =
new JavacInMemoryStep.SuggestBuildRules() {
@Override
- public ImmutableSet<String> apply(ImmutableSet<String> failedImports) {
+ public ImmutableSet<String> suggest(ProjectFilesystem filesystem,
+ ImmutableSet<String> failedImports) {
ImmutableSet.Builder<String> suggestedDeps = ImmutableSet.builder();
Set<String> remainingImports = Sets.newHashSet(failedImports);
@@ -691,7 +695,10 @@
boolean ruleCanSeeDep = transitiveNotDeclaredDep.isVisibleTo(
DefaultJavaLibraryRule.this.getBuildTarget());
if (ruleCanSeeDep &&
- isMissingBuildRule(transitiveNotDeclaredDep, remainingImports, jarResolver)) {
+ isMissingBuildRule(filesystem,
+ transitiveNotDeclaredDep,
+ remainingImports,
+ jarResolver)) {
suggestedDeps.add(transitiveNotDeclaredDep.getFullyQualifiedName());
}
// If we've wiped out all remaining imports, break the loop looking for them.
diff --git a/src/com/facebook/buck/java/JavacInMemoryStep.java b/src/com/facebook/buck/java/JavacInMemoryStep.java
index cab2500..adfe71c 100644
--- a/src/com/facebook/buck/java/JavacInMemoryStep.java
+++ b/src/com/facebook/buck/java/JavacInMemoryStep.java
@@ -125,8 +125,10 @@
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
- public static interface SuggestBuildRules extends
- Function<ImmutableSet<String>,ImmutableSet<String>> {}
+ public static interface SuggestBuildRules {
+ public ImmutableSet<String> suggest(ProjectFilesystem filesystem,
+ ImmutableSet<String> failedImports);
+ }
public JavacInMemoryStep(
String outputDirectory,
@@ -226,7 +228,8 @@
String firstOrderStderr = stderr.getContentsAsString(Charsets.UTF_8);
if (declaredDepsResult != 0) {
- int transitiveResult = buildWithClasspath(context, getClasspathEntries());
+ int transitiveResult = buildWithClasspath(context,
+ ImmutableSet.copyOf(transitiveClasspathEntries));
if (transitiveResult == 0) {
ImmutableSet<String> failedImports = findFailedImports(firstOrderStderr);
@@ -237,7 +240,8 @@
if (suggestBuildRules.isPresent()) {
context.getStdErr().println("Try adding the following deps:");
context.getStdErr().println(Joiner.on(LINE_SEPARATOR)
- .join(suggestBuildRules.get().apply(failedImports)));
+ .join(suggestBuildRules.get().suggest(context.getProjectFilesystem(),
+ failedImports)));
}
context.getStdErr().println();
context.getStdErr().println();
diff --git a/test/com/facebook/buck/java/DefaultJavaLibraryRuleIntegrationTest.java b/test/com/facebook/buck/java/DefaultJavaLibraryRuleIntegrationTest.java
index d9771e2..3b4921e 100644
--- a/test/com/facebook/buck/java/DefaultJavaLibraryRuleIntegrationTest.java
+++ b/test/com/facebook/buck/java/DefaultJavaLibraryRuleIntegrationTest.java
@@ -17,6 +17,7 @@
package com.facebook.buck.java;
import static java.nio.file.StandardOpenOption.WRITE;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
@@ -31,6 +32,7 @@
import com.facebook.buck.testutil.integration.ProjectWorkspace.ProcessResult;
import com.facebook.buck.testutil.integration.TestDataHelper;
import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.io.Files;
@@ -125,6 +127,64 @@
}
@Test
+ public void testBuildJavaLibraryWithTransitive() throws IOException {
+ workspace = TestDataHelper.createProjectWorkspaceForScenario(
+ this, "warn_on_transitive", tmp);
+ workspace.setUp();
+
+ // Run `buck build`.
+ ProcessResult buildResult = workspace.runBuckCommand("build", "//:raz", "-b", "TRANSITIVE");
+ buildResult.assertExitCode("Successful build should exit with 0.", 0);
+
+ workspace.verify();
+ }
+
+ @Test
+ public void testBuildJavaLibraryWithFirstOrder() throws IOException {
+ workspace = TestDataHelper.createProjectWorkspaceForScenario(
+ this, "warn_on_transitive", tmp);
+ workspace.setUp();
+
+ // Run `buck build`.
+ ProcessResult buildResult = workspace.runBuckCommand("build",
+ "//:raz",
+ "-b",
+ "FIRST_ORDER_ONLY");
+ buildResult.assertExitCode("Build should have failed", 1);
+
+ workspace.verify();
+ }
+
+ @Test
+ public void testBuildJavaLibraryWithWarnOnTransitive() throws IOException {
+ workspace = TestDataHelper.createProjectWorkspaceForScenario(
+ this, "warn_on_transitive", tmp);
+ workspace.setUp();
+
+ // Run `buck build`.
+ ProcessResult buildResult = workspace.runBuckCommand("build",
+ "//:raz",
+ "-b",
+ "WARN_ON_TRANSITIVE");
+
+ String expectedWarning = Joiner.on("\n").join(
+ "Rule //:raz builds with its transitive dependencies but not with its first order " +
+ "dependencies.",
+ "The following packages were missing:",
+ "Foo",
+ "Try adding the following deps:",
+ "//:foo");
+
+ buildResult.assertExitCode("Build should have succeeded with warnings.", 0);
+
+ assertThat(
+ buildResult.getStderr(),
+ containsString(expectedWarning));
+
+ workspace.verify();
+ }
+
+ @Test
public void testFileChangeThatDoesNotModifyAbiAvoidsRebuild() throws IOException {
workspace = TestDataHelper.createProjectWorkspaceForScenario(
this, "rulekey_changed_while_abi_stable", tmp);
diff --git a/test/com/facebook/buck/java/DefaultJavaLibraryRuleTest.java b/test/com/facebook/buck/java/DefaultJavaLibraryRuleTest.java
index ec2dee2..e6ec316 100644
--- a/test/com/facebook/buck/java/DefaultJavaLibraryRuleTest.java
+++ b/test/com/facebook/buck/java/DefaultJavaLibraryRuleTest.java
@@ -942,6 +942,7 @@
@Test
public void testSuggsetDepsReverseTopoSortRespected() {
BuildRuleResolver ruleResolver = new BuildRuleResolver();
+ ProjectFilesystem projectFilesystem = new ProjectFilesystem(tmp.getRoot());
BuildTarget libraryOneTarget = BuildTargetFactory.newInstance("//:libone");
DefaultJavaLibraryRule libraryOne = ruleResolver.buildAndAddToIndex(
@@ -991,7 +992,8 @@
assertTrue(suggestFn.isPresent());
assertEquals(ImmutableSet.of("//:parent", "//:libone"),
- suggestFn.get().apply(ImmutableSet.of("com.facebook.Foo", "com.facebook.Bar")));
+ suggestFn.get().suggest(projectFilesystem,
+ ImmutableSet.of("com.facebook.Foo", "com.facebook.Bar")));
EasyMock.verify(context);
}
@@ -1002,23 +1004,23 @@
private DefaultJavaLibraryRule.JarResolver createJarResolver(
final ImmutableMap<String, String> classToSymbols) {
- ImmutableSetMultimap.Builder<String, String> resolveMapBuilder =
+ ImmutableSetMultimap.Builder<Path, 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);
+ resolveMapBuilder.putAll(Paths.get(entry.getKey()), fullyQualified, packageName, className);
}
- final ImmutableSetMultimap<String, String> resolveMap = resolveMapBuilder.build();
+ final ImmutableSetMultimap<Path, String> resolveMap = resolveMapBuilder.build();
return new DefaultJavaLibraryRule.JarResolver() {
@Override
- public ImmutableSet<String> apply(String input) {
- if (resolveMap.containsKey(input)) {
- return resolveMap.get(input);
+ public ImmutableSet<String> resolve(ProjectFilesystem filesystem, Path relativeClassPath) {
+ if (resolveMap.containsKey(relativeClassPath)) {
+ return resolveMap.get(relativeClassPath);
} else {
return ImmutableSet.of();
}
diff --git a/test/com/facebook/buck/java/testdata/warn_on_transitive/BUCK b/test/com/facebook/buck/java/testdata/warn_on_transitive/BUCK
new file mode 100644
index 0000000..83b07e9
--- /dev/null
+++ b/test/com/facebook/buck/java/testdata/warn_on_transitive/BUCK
@@ -0,0 +1,21 @@
+# This is a canonical example of a genrule() that generates Java source code
+# paired with a java_library() that compiles the output of the genrule() along
+# with some hard-coded Java source code into a single library.
+
+java_library(
+ name = 'foo',
+ srcs = ['Foo.java'],
+)
+
+java_library(
+ name = 'bar',
+ srcs = ['Bar.java'],
+ deps = [':foo'],
+)
+
+java_library(
+ name = 'raz',
+ srcs = ['Raz.java'],
+ deps = [':bar'],
+)
+
diff --git a/test/com/facebook/buck/java/testdata/warn_on_transitive/Bar.java b/test/com/facebook/buck/java/testdata/warn_on_transitive/Bar.java
new file mode 100644
index 0000000..469ea7f
--- /dev/null
+++ b/test/com/facebook/buck/java/testdata/warn_on_transitive/Bar.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013-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.example;
+
+public class Bar {
+ private Foo foo;
+}
diff --git a/test/com/facebook/buck/java/testdata/warn_on_transitive/Foo.java b/test/com/facebook/buck/java/testdata/warn_on_transitive/Foo.java
new file mode 100644
index 0000000..cbee490
--- /dev/null
+++ b/test/com/facebook/buck/java/testdata/warn_on_transitive/Foo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013-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.example;
+
+public class Foo {}
diff --git a/test/com/facebook/buck/java/testdata/warn_on_transitive/Raz.java b/test/com/facebook/buck/java/testdata/warn_on_transitive/Raz.java
new file mode 100644
index 0000000..23e2023
--- /dev/null
+++ b/test/com/facebook/buck/java/testdata/warn_on_transitive/Raz.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013-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.example;
+
+public class Raz {
+ private Foo foo;
+ private Bar bar;
+}