Replace annotation_processors with plugins

Follow bazel's modules for annotation processors by introducing
a java_plugin module type that can contain extra metadata about
the annotation processor, the processor class and a flag to
specify if the annotation processor is compatible with the
turbine optimization.  Deprecate the annotation_processors
property, which took a list of java_library_host modules, in
favor of the plugins property, which takes a list of java_plugin
modules.  The annotation_processors property will be removed
once all uses have been replaced with plugins.

Bug: 77284273
Test: plugin_test.go
Test: m caliper
Change-Id: I37c1e80eba71ae2d6a06199fb102194a51994989
This commit is contained in:
Colin Cross 2019-01-21 21:37:16 -08:00
parent c4efd9cb55
commit be9cdb8d64
8 changed files with 222 additions and 9 deletions

View File

@ -249,6 +249,7 @@ bootstrap_go_package {
"java/jdeps.go",
"java/java_resources.go",
"java/kotlin.go",
"java/plugin.go",
"java/prebuilt_apis.go",
"java/proto.go",
"java/sdk.go",
@ -262,6 +263,7 @@ bootstrap_go_package {
"java/java_test.go",
"java/jdeps_test.go",
"java/kotlin_test.go",
"java/plugin_test.go",
"java/sdk_test.go",
],
pluginFor: ["soong_build"],

View File

@ -44,7 +44,7 @@ var (
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
`$processorpath $javacFlags $bootClasspath $classpath ` +
`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
@ -57,7 +57,7 @@ var (
Rspfile: "$out.rsp",
RspfileContent: "$in",
},
"javacFlags", "bootClasspath", "classpath", "processorpath", "srcJars", "srcJarDir",
"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
"outDir", "annoDir", "javaVersion")
turbine = pctx.AndroidStaticRule("turbine",
@ -141,6 +141,7 @@ type javaBuilderFlags struct {
bootClasspath classpath
classpath classpath
processorPath classpath
processor string
systemModules classpath
aidlFlags string
javaVersion string
@ -254,6 +255,12 @@ func transformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab
deps = append(deps, flags.classpath...)
deps = append(deps, flags.processorPath...)
// TODO(b/77284273): pass -processor:none if no plugins are listed
processor := ""
if flags.processor != "" {
processor = "-processor " + flags.processor
}
srcJarDir := "srcjars"
outDir := "classes"
annoDir := "anno"
@ -274,6 +281,7 @@ func transformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab
"bootClasspath": bootClasspath,
"classpath": flags.classpath.FormJavaClassPath("-classpath"),
"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
"processor": processor,
"srcJars": strings.Join(srcJars.Strings(), " "),
"srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
"outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),

View File

@ -114,11 +114,11 @@ type CompilerProperties struct {
// If set to true, include sources used to compile the module in to the final jar
Include_srcs *bool
// List of modules to use as annotation processors
// List of modules to use as annotation processors. Deprecated, use plugins instead.
Annotation_processors []string
// List of classes to pass to javac to use as annotation processors
Annotation_processor_classes []string
// List of modules to use as annotation processors
Plugins []string
// The number of Java source entries each Javac instance can process
Javac_shard_size *int64
@ -377,6 +377,7 @@ var (
staticLibTag = dependencyTag{name: "staticlib"}
libTag = dependencyTag{name: "javalib"}
annoTag = dependencyTag{name: "annotation processor"}
pluginTag = dependencyTag{name: "plugin"}
bootClasspathTag = dependencyTag{name: "bootclasspath"}
systemModulesTag = dependencyTag{name: "system modules"}
frameworkResTag = dependencyTag{name: "framework-res"}
@ -474,6 +475,10 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) {
{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
}, annoTag, j.properties.Annotation_processors...)
ctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
}, pluginTag, j.properties.Plugins...)
android.ExtractSourcesDeps(ctx, j.properties.Srcs)
android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
android.ExtractSourcesDeps(ctx, j.properties.Java_resources)
@ -563,6 +568,7 @@ type deps struct {
classpath classpath
bootClasspath classpath
processorPath classpath
processorClasses []string
staticJars android.Paths
staticHeaderJars android.Paths
staticResourceJars android.Paths
@ -573,6 +579,8 @@ type deps struct {
aidlPreprocess android.OptionalPath
kotlinStdlib android.Paths
kotlinAnnotations android.Paths
disableTurbine bool
}
func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
@ -712,6 +720,16 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps {
j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
case annoTag:
deps.processorPath = append(deps.processorPath, dep.ImplementationAndResourcesJars()...)
case pluginTag:
if plugin, ok := dep.(*Plugin); ok {
deps.processorPath = append(deps.processorPath, dep.ImplementationAndResourcesJars()...)
if plugin.pluginProperties.Processor_class != nil {
deps.processorClasses = append(deps.processorClasses, *plugin.pluginProperties.Processor_class)
}
deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
} else {
ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
}
case frameworkResTag:
if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
@ -859,6 +877,8 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB
flags.classpath = append(flags.classpath, deps.classpath...)
flags.processorPath = append(flags.processorPath, deps.processorPath...)
flags.processor = strings.Join(deps.processorClasses, ",")
if len(flags.bootClasspath) == 0 && ctx.Host() && flags.javaVersion != "1.9" &&
!Bool(j.properties.No_standard_libs) &&
inList(flags.javaVersion, []string{"1.6", "1.7", "1.8"}) {
@ -1020,7 +1040,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars ...android.Path
j.compiledSrcJars = srcJars
enable_sharding := false
if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") {
if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
enable_sharding = true
// Formerly, there was a check here that prevented annotation processors

View File

@ -83,6 +83,7 @@ func testContext(config android.Config, bp string,
ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
ctx.RegisterModuleType("java_plugin", android.ModuleFactoryAdaptor(PluginFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
ctx.RegisterModuleType("droiddoc", android.ModuleFactoryAdaptor(DroiddocFactory))

View File

@ -87,6 +87,7 @@ var kapt = pctx.AndroidGomaStaticRule("kapt",
`-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` +
`-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` +
`$kaptProcessorPath ` +
`$kaptProcessor ` +
`-Xbuild-file=$kotlinBuildFile && ` +
`${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources`,
CommandDeps: []string{
@ -100,7 +101,8 @@ var kapt = pctx.AndroidGomaStaticRule("kapt",
Rspfile: "$out.rsp",
RspfileContent: `$in`,
},
"kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile")
"kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "kaptProcessor",
"classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile")
// kotlinKapt performs Kotlin-compatible annotation processing. It takes .kt and .java sources and srcjars, and runs
// annotation processors over all of them, producing a srcjar of generated code in outputFile. The srcjar should be
@ -117,6 +119,11 @@ func kotlinKapt(ctx android.ModuleContext, outputFile android.WritablePath,
kaptProcessorPath := flags.processorPath.FormTurbineClasspath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
kaptProcessor := ""
if flags.processor != "" {
kaptProcessor = "-P plugin:org.jetbrains.kotlin.kapt3:processor=" + flags.processor
}
encodedJavacFlags := kaptEncodeFlags([][2]string{
{"-source", flags.javaVersion},
{"-target", flags.javaVersion},
@ -135,6 +142,7 @@ func kotlinKapt(ctx android.ModuleContext, outputFile android.WritablePath,
"srcJarDir": android.PathForModuleOut(ctx, "kapt", "srcJars").String(),
"kotlinBuildFile": android.PathForModuleOut(ctx, "kapt", "build.xml").String(),
"kaptProcessorPath": strings.Join(kaptProcessorPath, " "),
"kaptProcessor": kaptProcessor,
"kaptDir": android.PathForModuleOut(ctx, "kapt/gen").String(),
"encodedJavacFlags": encodedJavacFlags,
},

View File

@ -87,10 +87,10 @@ func TestKapt(t *testing.T) {
java_library {
name: "foo",
srcs: ["a.java", "b.kt"],
annotation_processors: ["bar"],
plugins: ["bar"],
}
java_library_host {
java_plugin {
name: "bar",
}
`)

50
java/plugin.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2019 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 java
import "android/soong/android"
func init() {
android.RegisterModuleType("java_plugin", PluginFactory)
}
// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
func PluginFactory() android.Module {
module := &Plugin{}
module.AddProperties(
&module.Module.properties,
&module.Module.protoProperties,
&module.pluginProperties)
InitJavaModule(module, android.HostSupported)
return module
}
type Plugin struct {
Library
pluginProperties PluginProperties
}
type PluginProperties struct {
// The optional name of the class that javac will use to run the annotation processor.
Processor_class *string
// If true, assume the annotation processor will generate classes that are referenced from outside the module.
// This necessitates disabling the turbine optimization on modules that use this plugin, which will reduce
// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
Generates_api *bool
}

124
java/plugin_test.go Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2019 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 java
import (
"android/soong/android"
"testing"
)
func TestNoPlugin(t *testing.T) {
ctx := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
}
`)
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
if turbine.Rule == nil {
t.Errorf("expected turbine to be enabled")
}
if javac.Args["processsorpath"] != "" {
t.Errorf("want empty processorpath, got %q", javac.Args["processorpath"])
}
// TODO(b/77284273): test for -processor:none if no plugins are enabled
if javac.Args["processor"] != "" {
t.Errorf("want no -processor argument, got %q", javac.Args["processor"])
}
}
func TestPlugin(t *testing.T) {
ctx := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
plugins: ["bar"],
}
java_plugin {
name: "bar",
processor_class: "com.bar",
srcs: ["b.java"],
}
`)
buildOS := android.BuildOs.String()
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
if turbine.Rule == nil {
t.Errorf("expected turbine to be enabled")
}
bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
if !inList(bar, javac.Implicits.Strings()) {
t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
}
if javac.Args["processorpath"] != "-processorpath "+bar {
t.Errorf("foo processorpath %q != '-processorpath %s'", javac.Args["processorpath"], bar)
}
if javac.Args["processor"] != "-processor com.bar" {
t.Errorf("foo processor %q != '-processor com.bar'", javac.Args["processor"])
}
}
func TestPluginGeneratesApi(t *testing.T) {
ctx := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
plugins: ["bar"],
}
java_plugin {
name: "bar",
processor_class: "com.bar",
generates_api: true,
srcs: ["b.java"],
}
`)
buildOS := android.BuildOs.String()
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
if turbine.Rule != nil {
t.Errorf("expected turbine to be disabled")
}
bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
if !inList(bar, javac.Implicits.Strings()) {
t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
}
if javac.Args["processorpath"] != "-processorpath "+bar {
t.Errorf("foo processorpath %q != '-processorpath %s'", javac.Args["processorpath"], bar)
}
if javac.Args["processor"] != "-processor com.bar" {
t.Errorf("foo processor %q != '-processor com.bar'", javac.Args["processor"])
}
}