Skip to content

Commit 60670b3

Browse files
authored
Add support for generated Java code with KSP (#1139)
* Add support for generating Java code with KSP * Add dagger to ksp sample project * Run java compilation action only when ksp generates java * Update ksp tests
1 parent d0c96cc commit 60670b3

31 files changed

+839
-248
lines changed

MODULE.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ maven.install(
6363
"com.google.auto.service:auto-service-annotations:1.0.1",
6464
"com.google.auto.value:auto-value:1.10.1",
6565
"com.google.auto.value:auto-value-annotations:1.10.1",
66-
"com.google.dagger:dagger:2.43.2",
67-
"com.google.dagger:dagger-compiler:2.43.2",
68-
"com.google.dagger:dagger-producers:2.43.2",
66+
"com.google.dagger:dagger:2.51",
67+
"com.google.dagger:dagger-compiler:2.51",
68+
"com.google.dagger:dagger-producers:2.51",
6969
"javax.annotation:javax.annotation-api:1.3.2",
7070
"javax.inject:javax.inject:1",
7171
"org.pantsbuild:jarjar:1.7.2",

docs/kotlin.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ kt_kotlinc_options(<a href="#kt_kotlinc_options-name">name</a>, <a href="#kt_kot
448448

449449
## kt_ksp_plugin
450450

451-
kt_ksp_plugin(<a href="#kt_ksp_plugin-name">name</a>, <a href="#kt_ksp_plugin-deps">deps</a>, <a href="#kt_ksp_plugin-processor_class">processor_class</a>)
451+
kt_ksp_plugin(<a href="#kt_ksp_plugin-name">name</a>, <a href="#kt_ksp_plugin-deps">deps</a>, <a href="#kt_ksp_plugin-generates_java">generates_java</a>, <a href="#kt_ksp_plugin-processor_class">processor_class</a>, <a href="#kt_ksp_plugin-target_embedded_compiler">target_embedded_compiler</a>)
452452

453453

454454
Define a KSP plugin for the Kotlin compiler to run. The plugin can then be referenced in the `plugins` attribute
@@ -481,7 +481,9 @@ kt_ksp_plugin(<a href="#kt_ksp_plugin-name">name</a>, <a href="#kt_ksp_plugin-de
481481
| :------------- | :------------- | :------------- | :------------- | :------------- |
482482
|<a id="kt_ksp_plugin-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
483483
|<a id="kt_ksp_plugin-deps"></a>deps | The list of libraries to be added to the compiler's plugin classpath | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | [] |
484+
|<a id="kt_ksp_plugin-generates_java"></a>generates_java | Runs Java compilation action for plugin generating Java output. | Boolean | optional | False |
484485
|<a id="kt_ksp_plugin-processor_class"></a>processor_class | The fully qualified class name that the Java compiler uses as an entry point to the annotation processor. | String | required | |
486+
|<a id="kt_ksp_plugin-target_embedded_compiler"></a>target_embedded_compiler | Plugin was compiled against the embeddable kotlin compiler. These plugins expect shaded kotlinc dependencies, and will fail when running against a non-embeddable compiler. | Boolean | optional | False |
485487

486488

487489
<a id="#kt_plugin_cfg"></a>

examples/ksp/BUILD

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("@bazel_skylib//rules:build_test.bzl", "build_test")
2-
load("@rules_java//java:defs.bzl", "java_binary", "java_plugin")
2+
load("@rules_java//java:defs.bzl", "java_binary")
33

44
# Copyright 2018 The Bazel Authors. All rights reserved.
55
#
@@ -14,55 +14,99 @@ load("@rules_java//java:defs.bzl", "java_binary", "java_plugin")
1414
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
17-
load("@rules_kotlin//kotlin:core.bzl", "define_kt_toolchain", "kt_compiler_plugin", "kt_ksp_plugin", "kt_plugin_cfg")
17+
load("@rules_kotlin//kotlin:core.bzl", "define_kt_toolchain")
1818
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
1919

2020
package(default_visibility = ["//visibility:public"])
2121

2222
define_kt_toolchain(name = "kotlin_toolchain")
2323

24-
java_plugin(
25-
name = "autovalue",
26-
generates_api = True,
27-
processor_class = "com.google.auto.value.processor.AutoValueProcessor",
28-
deps = ["@maven//:com_google_auto_value_auto_value"],
24+
# Generate a srcjar to validate intellij plugin correctly attaches it.
25+
genrule(
26+
name = "tea_lib_src",
27+
outs = ["tea_lib_src.srcjar"],
28+
cmd = """
29+
cat << EOF > TeaPot.kt
30+
package tea
31+
object TeaPot {
32+
fun isEmpty() = true
33+
}
34+
EOF
35+
$(JAVABASE)/bin/jar -cf $@ TeaPot.kt
36+
rm TeaPot.kt
37+
""",
38+
toolchains = ["@bazel_tools//tools/jdk:current_host_java_runtime"],
2939
)
3040

31-
kt_ksp_plugin(
32-
name = "moshi-kotlin-codegen",
33-
processor_class = "com.squareup.moshi.kotlin.codegen.ksp.JsonClassSymbolProcessorProvider",
34-
deps = [
35-
"@maven//:com_squareup_moshi_moshi",
36-
"@maven//:com_squareup_moshi_moshi_kotlin",
37-
"@maven//:com_squareup_moshi_moshi_kotlin_codegen",
38-
],
41+
genrule(
42+
name = "chai_lib_src",
43+
outs = ["chai_lib_src.srcjar"],
44+
cmd = """
45+
cat << EOF > ChaiCup.kt
46+
package chai
47+
object ChaiCup {
48+
fun isEmpty() = true
49+
}
50+
EOF
51+
$(JAVABASE)/bin/jar -cf $@ ChaiCup.kt
52+
rm ChaiCup.kt
53+
""",
54+
toolchains = ["@bazel_tools//tools/jdk:current_host_java_runtime"],
3955
)
4056

41-
kt_ksp_plugin(
42-
name = "autoservice",
43-
processor_class = "dev.zacsweers.autoservice.ksp.AutoServiceSymbolProcessor$Provider",
44-
deps = [
45-
"@maven//:com_google_auto_service_auto_service_annotations",
46-
"@maven//:dev_zacsweers_autoservice_auto_service_ksp",
47-
],
57+
genrule(
58+
name = "genereated_module_src",
59+
outs = ["genarated_module_src.srcjar"],
60+
cmd = """
61+
cat << EOF > GeneratedModule.kt
62+
package generated
63+
64+
import dagger.Provides
65+
import dagger.Module
66+
67+
@Module
68+
object GeneratedModule {
69+
@Provides
70+
fun provideString() = "Hello Coffee"
71+
}
72+
EOF
73+
$(JAVABASE)/bin/jar -cf $@ GeneratedModule.kt
74+
rm GeneratedModule.kt
75+
""",
76+
toolchains = ["@bazel_tools//tools/jdk:current_host_java_runtime"],
77+
)
78+
79+
kt_jvm_library(
80+
name = "generated_lib",
81+
srcs = [":genereated_module_src"],
82+
plugins = ["//third_party:dagger_ksp_plugin"],
83+
deps = ["@maven//:com_google_dagger_dagger"],
4884
)
4985

5086
kt_jvm_library(
5187
name = "coffee_lib",
5288
srcs = glob([
5389
"*.kt",
5490
"*.java",
55-
]),
91+
]) + [
92+
# Adding a file ending with .srcjar is how code generation patterns are implemented.
93+
":tea_lib_src",
94+
":chai_lib_src",
95+
],
5696
plugins = [
57-
"//:moshi-kotlin-codegen",
58-
"//:autovalue",
59-
"//:autoservice",
97+
"//third_party:dagger_ksp_plugin",
98+
"//third_party:moshi-kotlin-codegen",
99+
"//third_party:autovalue",
100+
"//third_party:autoservice",
60101
],
61102
deps = [
103+
":generated_lib",
62104
"@maven//:com_google_auto_service_auto_service_annotations",
63105
"@maven//:com_google_auto_value_auto_value_annotations",
106+
"@maven//:com_google_dagger_dagger",
64107
"@maven//:com_squareup_moshi_moshi",
65108
"@maven//:com_squareup_moshi_moshi_kotlin",
109+
"@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core",
66110
],
67111
)
68112

@@ -82,60 +126,12 @@ build_test(
82126
],
83127
)
84128

85-
kt_compiler_plugin(
86-
name = "ksp",
87-
compile_phase = True,
88-
id = "com.google.devtools.ksp.symbol-processing",
89-
options = {
90-
"apclasspath": "{classpath}",
91-
# projectBaseDir shouldn't matter because incremental is disabled
92-
"projectBaseDir": "{temp}",
93-
# Disable incremental mode
94-
"incremental": "false",
95-
# Directory where class files are written to. Files written to this directory are class
96-
# files being written directly from the annotation processor, not Kotlinc
97-
"classOutputDir": "{generatedClasses}",
98-
# Directory where generated Java sources files are written to
99-
"javaOutputDir": "{generatedSources}",
100-
# Directory where generated Kotlin sources files are written to
101-
"kotlinOutputDir": "{generatedSources}",
102-
# Directory where META-INF data is written to. This might not be the most ideal place to
103-
# write this. Maybe just directly to the classes directory?
104-
"resourceOutputDir": "{generatedSources}",
105-
# TODO(bencodes) Not sure what this directory is yet.
106-
"kspOutputDir": "{temp}",
107-
# Directory to write KSP caches. Shouldn't matter because incremental is disabled
108-
"cachesDir": "{temp}",
109-
# Include in compilation as an example. This should be processed in the stubs phase.
110-
"withCompilation": "true",
111-
# Set returnOkOnError to false because we want to fail the build if there are any errors
112-
"returnOkOnError": "false",
113-
"allWarningsAsErrors": "false",
114-
},
115-
deps = [
116-
"@rules_kotlin//kotlin/compiler:symbol-processing-api",
117-
"@rules_kotlin//kotlin/compiler:symbol-processing-cmdline",
118-
],
119-
)
120-
121-
kt_plugin_cfg(
122-
name = "ksp_moshi",
123-
options = {
124-
},
125-
plugin = ":ksp",
126-
deps = [
127-
"@maven//:com_squareup_moshi_moshi",
128-
"@maven//:com_squareup_moshi_moshi_kotlin",
129-
"@maven//:com_squareup_moshi_moshi_kotlin_codegen",
130-
],
131-
)
132-
133129
kt_jvm_library(
134130
name = "raw_ksp_coffee_app_lib",
135131
srcs = ["CoffeeAppModel.kt"],
136132
plugins = [
137-
"//:ksp",
138-
"//:ksp_moshi",
133+
"//third_party:ksp",
134+
"//third_party:ksp_moshi",
139135
],
140136
deps = [
141137
"@maven//:com_google_auto_service_auto_service_annotations",

examples/ksp/CoffeeApp.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,21 @@
1616
package coffee
1717

1818
import com.squareup.moshi.Moshi
19+
import generated.GeneratedModule
20+
import dagger.Component
21+
import kotlinx.coroutines.runBlocking
22+
import tea.TeaPot
23+
import chai.ChaiCup
24+
import javax.inject.Singleton
1925

2026
class CoffeeApp {
2127

28+
@Singleton
29+
@Component(modules = [DripCoffeeModule::class, GeneratedModule::class])
30+
interface CoffeeShop {
31+
fun maker(): CoffeeMaker
32+
}
33+
2234
companion object {
2335

2436
private val adapter = CoffeeAppModelJsonAdapter(Moshi.Builder().build())
@@ -28,9 +40,14 @@ class CoffeeApp {
2840

2941
@JvmStatic
3042
fun main(args: Array<String>) {
31-
println(
32-
adapter.toJson(d.coffeeAppModel())
33-
)
43+
println("Coffee model: ${adapter.toJson(d.coffeeAppModel())}")
44+
45+
if (TeaPot.isEmpty() && ChaiCup.isEmpty()) {
46+
val coffeeShop = DaggerCoffeeApp_CoffeeShop.builder().build()
47+
runBlocking {
48+
coffeeShop.maker().brew()
49+
}
50+
}
3451
}
3552
}
3653
}

examples/ksp/CoffeeMaker.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2018 The Bazel Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package coffee
17+
18+
import dagger.Lazy
19+
import kotlinx.coroutines.Dispatchers
20+
import kotlinx.coroutines.withContext
21+
import javax.inject.Inject
22+
23+
class CoffeeMaker @Inject internal constructor(
24+
// Create a possibly costly heater only when we use it.
25+
private val heater: Lazy<Heater>,
26+
private val pump: Pump,
27+
private val string: String
28+
) {
29+
30+
suspend fun brew() {
31+
// this function is async to verify intellij support for coroutines.
32+
withContext(Dispatchers.Default) {
33+
heater.get().on()
34+
pump.pump()
35+
println(" [_]P coffee! [_]P ")
36+
println(string)
37+
heater.get().off()
38+
}
39+
}
40+
}

examples/ksp/DripCoffeeModule.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2018 The Bazel Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package coffee
17+
18+
import dagger.Module
19+
import dagger.Provides
20+
import javax.inject.Singleton
21+
22+
@Module(includes = arrayOf(PumpModule::class))
23+
internal class DripCoffeeModule {
24+
@Provides
25+
@Singleton
26+
fun provideHeater(): Heater {
27+
return ElectricHeater()
28+
}
29+
}

examples/ksp/ElectricHeater.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2018 The Bazel Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package coffee
17+
18+
internal class ElectricHeater : Heater {
19+
override var isHot: Boolean = false
20+
21+
override fun on() {
22+
println("~ ~ ~ heating ~ ~ ~")
23+
this.isHot = true
24+
}
25+
26+
override fun off() {
27+
this.isHot = false
28+
}
29+
}

examples/ksp/Heater.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2018 The Bazel Authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package coffee
17+
18+
internal interface Heater {
19+
val isHot: Boolean
20+
fun on()
21+
fun off()
22+
}

0 commit comments

Comments
 (0)