diff --git a/Android.bp b/Android.bp index cc28c9893..df3babcc1 100644 --- a/Android.bp +++ b/Android.bp @@ -222,6 +222,7 @@ bootstrap_go_package { ], srcs: [ "java/config/config.go", + "java/config/errorprone.go", "java/config/makevars.go", ], } diff --git a/android/package_ctx.go b/android/package_ctx.go index 2bc98aaf9..6743fb3fe 100644 --- a/android/package_ctx.go +++ b/android/package_ctx.go @@ -16,6 +16,7 @@ package android import ( "fmt" + "strings" "github.com/google/blueprint" "github.com/google/blueprint/pathtools" @@ -75,6 +76,25 @@ func (p AndroidPackageContext) SourcePathVariable(name, path string) blueprint.V }) } +// SourcePathsVariable returns a Variable whose value is the source directory +// appended with the supplied paths, joined with separator. It may only be +// called during a Go package's initialization - either from the init() +// function or as part of a package-scoped variable's initialization. +func (p AndroidPackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable { + return p.VariableFunc(name, func(config interface{}) (string, error) { + ctx := &configErrorWrapper{p, config.(Config), []error{}} + var ret []string + for _, path := range paths { + p := safePathForSource(ctx, path) + if len(ctx.errors) > 0 { + return "", ctx.errors[0] + } + ret = append(ret, p.String()) + } + return strings.Join(ret, separator), nil + }) +} + // SourcePathVariableWithEnvOverride returns a Variable whose value is the source directory // appended with the supplied path, or the value of the given environment variable if it is set. // The environment variable is not required to point to a path inside the source tree. diff --git a/java/builder.go b/java/builder.go index efe0a6bbe..b7ff3abe5 100644 --- a/java/builder.go +++ b/java/builder.go @@ -49,6 +49,25 @@ var ( }, "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 @@ func TransformJavaToClasses(ctx android.ModuleContext, srcFiles android.Paths, s 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 000000000..da9b77535 --- /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 ac88020cc..e2e15d463 100644 --- a/java/java.go +++ b/java/java.go @@ -330,6 +330,8 @@ func (j *Module) compile(ctx android.ModuleContext) { 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 @@ func (j *Module) compile(ctx android.ModuleContext) { 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 @@ func (j *Module) compile(ctx android.ModuleContext) { 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 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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)