Add support for kotlin plugins

Use the already existing hooks used by the compose
compiler plugin to support kotlin plugins bundled
alongside the compiler such as kotlin-serialize-compiler

Test: m, sample app with kotlin-serialize-compiler
Change-Id: I4d5fa6cd42acfc90ef437222e9e07f61c259d565
diff --git a/java/base.go b/java/base.go
index a9399cb..19f6c5d 100644
--- a/java/base.go
+++ b/java/base.go
@@ -113,6 +113,9 @@
 	// List of modules to use as annotation processors
 	Plugins []string
 
+	// List of modules to use as kotlin plugin
+	Kotlin_plugins []string
+
 	// List of modules to export to libraries that directly depend on this library as annotation
 	// processors.  Note that if the plugins set generates_api: true this will disable the turbine
 	// optimization on modules that depend on this module, which will reduce parallelism and cause
@@ -872,6 +875,7 @@
 	}
 
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag, j.properties.Kotlin_plugins...)
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...)
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
 
@@ -904,7 +908,7 @@
 
 	if j.useCompose(ctx) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
-			"androidx.compose.compiler_compiler-hosted")
+			"androidx.compose.compiler_compiler-hosted-plugin")
 	}
 }
 
@@ -2499,7 +2503,11 @@
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
 			case kotlinPluginTag:
-				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
+				if _, ok := module.(*KotlinPlugin); ok {
+					deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
+				} else {
+					ctx.PropertyErrorf("kotlin_plugins", "%q is not a kotlin_plugin module", otherName)
+				}
 			case syspropPublicStubDepTag:
 				// This is a sysprop implementation library, forward the JavaInfoProvider from
 				// the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index f6e7fca..45eac01 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -500,8 +500,8 @@
 			name: "androidx.compose.runtime_runtime",
 		}
 
-		java_library_host {
-			name: "androidx.compose.compiler_compiler-hosted",
+		kotlin_plugin {
+			name: "androidx.compose.compiler_compiler-hosted-plugin",
 		}
 
 		java_library {
@@ -523,7 +523,7 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output
+	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted-plugin", buildOS+"_common").Rule("combineJar").Output
 	withCompose := result.ModuleForTests("withcompose", "android_common")
 	noCompose := result.ModuleForTests("nocompose", "android_common")
 
@@ -542,3 +542,50 @@
 	android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin",
 		noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
 }
+
+func TestKotlinPlugin(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		kotlin_plugin {
+			name: "kotlin_plugin",
+		}
+
+		java_library {
+			name: "with_kotlin_plugin",
+			srcs: ["a.kt"],
+			plugins: ["plugin"],
+			kotlin_plugins: ["kotlin_plugin"],
+		}
+
+		java_library {
+			name: "no_kotlin_plugin",
+			srcs: ["a.kt"],
+		}
+
+		java_plugin {
+			name: "plugin",
+		}
+	`)
+
+	buildOS := result.Config.BuildOS.String()
+
+	kotlinPlugin := result.ModuleForTests("kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
+	withKotlinPlugin := result.ModuleForTests("with_kotlin_plugin", "android_common")
+	noKotlinPlugin := result.ModuleForTests("no_kotlin_plugin", "android_common")
+
+	android.AssertStringListContains(t, "missing plugin compiler dependency",
+		withKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringDoesContain(t, "missing kotlin plugin",
+		withKotlinPlugin.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+kotlinPlugin.String())
+
+	android.AssertStringListContains(t, "missing kapt kotlin plugin dependency",
+		withKotlinPlugin.Rule("kapt").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringListDoesNotContain(t, "unexpected kotlin plugin dependency",
+		noKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringDoesNotContain(t, "unexpected kotlin plugin",
+		noKotlinPlugin.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+kotlinPlugin.String())
+}
diff --git a/java/plugin.go b/java/plugin.go
index 9c4774a..610c9fd 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -24,6 +24,7 @@
 
 func registerJavaPluginBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
+	ctx.RegisterModuleType("kotlin_plugin", KotlinPluginFactory)
 }
 
 func PluginFactory() android.Module {
@@ -37,6 +38,16 @@
 	return module
 }
 
+func KotlinPluginFactory() android.Module {
+	module := &KotlinPlugin{}
+
+	module.addHostProperties()
+
+	InitJavaModule(module, android.HostSupported)
+
+	return module
+}
+
 // Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor.
 type Plugin struct {
 	Library
@@ -53,3 +64,8 @@
 	// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
 	Generates_api *bool
 }
+
+// Plugin describes a kotlin_plugin module, a host java/kotlin library that will be used by kotlinc as a compiler plugin.
+type KotlinPlugin struct {
+	Library
+}