Add error-prone support

Add support for compiling java sources with the error-prone tool.

Test: m -j checkbuild
Change-Id: Ieb4ee0e05f8f34a52ed7bcf1c7cbacf1c9c4d0b5
diff --git a/java/builder.go b/java/builder.go
index efe0a6b..b7ff3ab 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -49,6 +49,25 @@
 		},
 		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
 
+	errorprone = pctx.AndroidStaticRule("errorprone",
+		blueprint.RuleParams{
+			Command: `rm -rf "$outDir" "$annoDir" && mkdir -p "$outDir" "$annoDir" && ` +
+				`${config.ErrorProneCmd}` +
+				`$javacFlags $bootClasspath $classpath ` +
+				`-source $javaVersion -target $javaVersion ` +
+				`-d $outDir -s $annoDir @$out.rsp && ` +
+				`find $outDir -type f | sort | ${config.JarArgsCmd} $outDir > $out`,
+			CommandDeps: []string{
+				"${config.JavaCmd}",
+				"${config.ErrorProneJavacJar}",
+				"${config.ErrorProneJar}",
+				"${config.JarArgsCmd}",
+			},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+		},
+		"javacFlags", "bootClasspath", "classpath", "outDir", "annoDir", "javaVersion")
+
 	jar = pctx.AndroidStaticRule("jar",
 		blueprint.RuleParams{
 			Command:     `${config.JarCmd} $operation ${out}.tmp $manifest $jarArgs && ${config.Zip2ZipCmd} -t -i ${out}.tmp -o ${out} && rm ${out}.tmp`,
@@ -144,12 +163,41 @@
 	return jarSpec{classFileList}
 }
 
+func RunErrorProne(ctx android.ModuleContext, srcFiles android.Paths, srcFileLists android.Paths,
+	flags javaBuilderFlags, deps android.Paths) android.Path {
+
+	classDir := android.PathForModuleOut(ctx, "classes-errorprone")
+	annoDir := android.PathForModuleOut(ctx, "anno-errorprone")
+	classFileList := android.PathForModuleOut(ctx, "classes-errorprone.list")
+
+	javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@")
+
+	deps = append(deps, srcFileLists...)
+
+	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+		Rule:        errorprone,
+		Description: "errorprone",
+		Output:      classFileList,
+		Inputs:      srcFiles,
+		Implicits:   deps,
+		Args: map[string]string{
+			"javacFlags":    javacFlags,
+			"bootClasspath": flags.bootClasspath,
+			"classpath":     flags.classpath,
+			"outDir":        classDir.String(),
+			"annoDir":       annoDir.String(),
+			"javaVersion":   flags.javaVersion,
+		},
+	})
+
+	return classFileList
+}
+
 func TransformClassesToJar(ctx android.ModuleContext, classes []jarSpec,
-	manifest android.OptionalPath) android.Path {
+	manifest android.OptionalPath, deps android.Paths) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, "classes-full-debug.jar")
 
-	deps := android.Paths{}
 	jarArgs := []string{}
 
 	for _, j := range classes {
diff --git a/java/config/errorprone.go b/java/config/errorprone.go
new file mode 100644
index 0000000..da9b775
--- /dev/null
+++ b/java/config/errorprone.go
@@ -0,0 +1,97 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// 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 config
+
+import (
+	"strings"
+)
+
+func init() {
+	pctx.SourcePathVariable("ErrorProneJavacJar", "external/error_prone/javac/javac-9-dev-r3297-4.jar")
+	pctx.SourcePathVariable("ErrorProneJar", "external/error_prone/error_prone/error_prone_core-2.0.19-with-dependencies.jar")
+	pctx.SourcePathsVariable("ErrorProneClasspath", ":",
+		"external/error_prone/error_prone/error_prone_annotations-2.0.19.jar",
+		"external/error_prone/checkerframework/dataflow-1.8.10.jar",
+		"external/error_prone/checkerframework/javacutil-1.8.10.jar",
+		"external/error_prone/jFormatString/jFormatString-3.0.0.jar")
+
+	// The checks that are fatal to the build.
+	pctx.StaticVariable("ErrorProneChecksError", strings.Join([]string{
+		"-Xep:AsyncCallableReturnsNull:ERROR",
+		"-Xep:AsyncFunctionReturnsNull:ERROR",
+		"-Xep:BundleDeserializationCast:ERROR",
+		"-Xep:CompatibleWithAnnotationMisuse:ERROR",
+		"-Xep:CompileTimeConstant:ERROR",
+		"-Xep:DaggerProvidesNull:ERROR",
+		"-Xep:DoNotCall:ERROR",
+		"-Xep:ForOverride:ERROR",
+		"-Xep:FunctionalInterfaceMethodChanged:ERROR",
+		"-Xep:FuturesGetCheckedIllegalExceptionType:ERROR",
+		"-Xep:GuiceAssistedInjectScoping:ERROR",
+		"-Xep:GuiceAssistedParameters:ERROR",
+		"-Xep:GuiceInjectOnFinalField:ERROR",
+		"-Xep:Immutable:ERROR",
+		"-Xep:ImmutableModification:ERROR",
+		"-Xep:IncompatibleArgumentType:ERROR",
+		"-Xep:IndexOfChar:ERROR",
+		"-Xep:InjectMoreThanOneScopeAnnotationOnClass:ERROR",
+		"-Xep:JavaxInjectOnAbstractMethod:ERROR",
+		"-Xep:JUnit4SetUpNotRun:ERROR",
+		"-Xep:JUnit4TearDownNotRun:ERROR",
+		"-Xep:JUnit4TestNotRun:ERROR",
+		"-Xep:JUnitAssertSameCheck:ERROR",
+		"-Xep:LiteByteStringUtf8:ERROR",
+		"-Xep:LoopConditionChecker:ERROR",
+		"-Xep:MockitoCast:ERROR",
+		"-Xep:MockitoUsage:ERROR",
+		"-Xep:MoreThanOneInjectableConstructor:ERROR",
+		"-Xep:MustBeClosedChecker:ERROR",
+		"-Xep:NonCanonicalStaticImport:ERROR",
+		"-Xep:NonFinalCompileTimeConstant:ERROR",
+		"-Xep:OptionalEquality:ERROR",
+		"-Xep:OverlappingQualifierAndScopeAnnotation:ERROR",
+		"-Xep:PackageInfo:ERROR",
+		"-Xep:PreconditionsCheckNotNull:ERROR",
+		"-Xep:PreconditionsCheckNotNullPrimitive:ERROR",
+		"-Xep:ProtoFieldNullComparison:ERROR",
+		"-Xep:ProvidesMethodOutsideOfModule:ERROR",
+		"-Xep:RestrictedApiChecker:ERROR",
+		"-Xep:SelfAssignment:ERROR",
+		"-Xep:StreamToString:ERROR",
+		"-Xep:SuppressWarningsDeprecated:ERROR",
+		"-Xep:ThrowIfUncheckedKnownChecked:ERROR",
+		"-Xep:ThrowNull:ERROR",
+		"-Xep:TypeParameterQualifier:ERROR",
+		"-Xep:UnnecessaryTypeArgument:ERROR",
+		"-Xep:UnusedAnonymousClass:ERROR",
+	}, " "))
+
+	pctx.StaticVariable("ErrorProneFlags", strings.Join([]string{
+		"com.google.errorprone.ErrorProneCompiler",
+		"-Xdiags:verbose",
+		"-XDcompilePolicy=simple",
+		"-XDallowBetterNullChecks=false",
+		"-XDusePolyAttribution=true",
+		"-XDuseStrictMethodClashCheck=true",
+		"-XDuseStructuralMostSpecificResolution=true",
+		"-XDuseGraphInference=true",
+		"-Xmaxwarns 100000",
+		"-XDandroidCompatible=true",
+		"-XepAllErrorsAsWarnings",
+	}, " "))
+
+	pctx.StaticVariable("ErrorProneCmd",
+		"${JavaCmd} -Xbootclasspath/p:${ErrorProneJavacJar} -cp ${ErrorProneJar}:${ErrorProneClasspath} ${ErrorProneFlags} ${ErrorProneChecksError}")
+}
diff --git a/java/java.go b/java/java.go
index ac88020..e2e15d4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -330,6 +330,8 @@
 
 	srcFileLists = append(srcFileLists, j.ExtraSrcLists...)
 
+	var extraJarDeps android.Paths
+
 	if len(srcFiles) > 0 {
 		// Compile java sources into .class files
 		classes := TransformJavaToClasses(ctx, srcFiles, srcFileLists, flags, deps)
@@ -337,6 +339,17 @@
 			return
 		}
 
+		if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
+			// If error-prone is enabled, add an additional rule to compile the java files into
+			// a separate set of classes (so that they don't overwrite the normal ones and require
+			// a rebuild when error-prone is turned off).  Add the classes as a dependency to
+			// the jar command so the two compiles can run in parallel.
+			// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
+			//    enable error-prone without affecting the output class files.
+			errorprone := RunErrorProne(ctx, srcFiles, srcFileLists, flags, deps)
+			extraJarDeps = append(extraJarDeps, errorprone)
+		}
+
 		classJarSpecs = append([]jarSpec{classes}, classJarSpecs...)
 	}
 
@@ -349,7 +362,7 @@
 	allJarSpecs = append(allJarSpecs, resourceJarSpecs...)
 
 	// Combine classes + resources into classes-full-debug.jar
-	outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest)
+	outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest, extraJarDeps)
 	if ctx.Failed() {
 		return
 	}
@@ -575,7 +588,7 @@
 		j.resourceJarSpecs = append(j.resourceJarSpecs, resourceJarSpec)
 	}
 
-	j.combinedClasspathFile = TransformClassesToJar(ctx, j.classJarSpecs, android.OptionalPath{})
+	j.combinedClasspathFile = TransformClassesToJar(ctx, j.classJarSpecs, android.OptionalPath{}, nil)
 
 	ctx.InstallFileName(android.PathForModuleInstall(ctx, "framework"),
 		ctx.ModuleName()+".jar", j.combinedClasspathFile)