Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add kt_plugin rule #308

Merged
merged 13 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,42 @@ kotlin_repositories() # if you want the default. Otherwise see custom kotlinc di
kt_register_toolchains() # to use the default toolchain, otherwise see toolchains below
```

# Kotlin compiler plugins

The `kt_compiler_plugin` rule allows running Kotlin compiler plugins, such as no-arg, sam-with-receiver and allopen.

For example, you can add allopen to your project like this:
```python
load("//kotlin:kotlin.bzl", "kt_compiler_plugin", "kt_jvm_library")

kt_compiler_plugin(
name = "open_for_testing_plugin",
id = "org.jetbrains.kotlin.allopen",
options = {
"annotation": "plugin.allopen.OpenForTesting",
},
deps = [
"@com_github_jetbrains_kotlin//:allopen-compiler-plugin",
],
)

kt_jvm_library(
name = "user",
srcs = ["User.kt"], # The User class is annotated with OpenForTesting
plugins = [
":open_for_testing_plugin",
],
deps = [
":open_for_testing", # This contains the annotation (plugin.allopen.OpenForTesting)
],
)
```

Full examples of using compiler plugins can be found [here](examples/plugin).

## Examples

Examples can be found in the [examples directory](https://github.com/bazelbuild/rules_kotlin/tree/master/examples), including usage with Android, Dagger, Node-JS, etc.
Examples can be found in the [examples directory](https://github.com/bazelbuild/rules_kotlin/tree/master/examples), including usage with Android, Dagger, Node-JS, Kotlin compiler plugins, etc.

# History

Expand Down
36 changes: 36 additions & 0 deletions examples/plugin/src/allopen/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
load("//kotlin:kotlin.bzl", "kt_compiler_plugin", "kt_jvm_library")

kt_compiler_plugin(
name = "open_for_testing_plugin",
id = "org.jetbrains.kotlin.allopen",
options = {
"annotation": "plugin.allopen.OpenForTesting",
},
deps = [
"@com_github_jetbrains_kotlin//:allopen-compiler-plugin",
],
)

kt_jvm_library(
name = "open_for_testing",
srcs = ["OpenForTesting.kt"],
)

kt_jvm_library(
name = "user",
srcs = ["User.kt"],
plugins = [
":open_for_testing_plugin",
],
deps = [
":open_for_testing",
],
)

kt_jvm_library(
name = "user_is_open_test",
srcs = ["UserIsOpenTest.kt"],
deps = [
":user",
],
)
3 changes: 3 additions & 0 deletions examples/plugin/src/allopen/OpenForTesting.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package plugin.allopen

annotation class OpenForTesting
9 changes: 9 additions & 0 deletions examples/plugin/src/allopen/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package plugin.allopen;

import java.util.*

@OpenForTesting
data class User(
val userId: UUID,
val emails: String
)
5 changes: 5 additions & 0 deletions examples/plugin/src/allopen/UserIsOpenTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package plugin.allopen

import java.util.*

class Subclass : User(UUID.randomUUID(), "[email protected]")
38 changes: 38 additions & 0 deletions examples/plugin/src/noarg/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
load("//kotlin:kotlin.bzl", "kt_compiler_plugin", "kt_jvm_library", "kt_jvm_test")

kt_compiler_plugin(
name = "no_arg_plugin",
id = "org.jetbrains.kotlin.noarg",
options = {
"annotation": "plugin.noarg.NoArgConstructor",
},
deps = [
"@com_github_jetbrains_kotlin//:noarg-compiler-plugin",
],
)

kt_jvm_library(
name = "no_arg_constructor",
srcs = ["NoArgConstructor.kt"],
)

kt_jvm_library(
name = "user",
srcs = ["User.kt"],
plugins = [":no_arg_plugin"],
deps = [
":no_arg_constructor",
],
)

# The no-arg constructor that is generated cannot be compiled against, but should be discoverable at runtime.
kt_jvm_test(
name = "user_has_noarg_constructor_test",
srcs = ["UserHasNoargConstructorTest.kt"],
test_class = "plugin.noarg.UserHasNoargConstructorTest",
deps = [
":user",
"@com_github_jetbrains_kotlin//:kotlin-reflect",
"@kotlin_rules_maven//:junit_junit",
],
)
3 changes: 3 additions & 0 deletions examples/plugin/src/noarg/NoArgConstructor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package plugin.noarg

annotation class NoArgConstructor
9 changes: 9 additions & 0 deletions examples/plugin/src/noarg/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package plugin.noarg;

import java.util.*

@NoArgConstructor
data class User(
val userId: UUID,
val emails: String
)
13 changes: 13 additions & 0 deletions examples/plugin/src/noarg/UserHasNoargConstructorTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package plugin.noarg

import org.junit.*
import java.lang.Exception

class UserHasNoargConstructorTest {
@Test
fun userShouldHaveNoargConstructor() {
if (User::class.java.constructors.none { it.parameters.isEmpty() }) {
throw Exception("Expected an empty constructor to exist")
}
}
}
31 changes: 31 additions & 0 deletions examples/plugin/src/sam_with_receiver/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
load("//kotlin:kotlin.bzl", "kt_compiler_plugin", "kt_jvm_library")
load("@rules_java//java:defs.bzl", "java_library")

kt_compiler_plugin(
name = "sam_with_receiver_plugin",
id = "org.jetbrains.kotlin.samWithReceiver",
options = {
"annotation": "plugin.sam_with_receiver.SamWithReceiver",
},
deps = [
"@com_github_jetbrains_kotlin//:sam-with-receiver-compiler-plugin",
],
)

kt_jvm_library(
name = "sam_with_receiver",
srcs = ["SamWithReceiver.kt"],
)

java_library(
name = "runner",
srcs = ["Runner.java"],
deps = [":sam_with_receiver"],
)

kt_jvm_library(
name = "runner_test",
srcs = ["RunnerTest.kt"],
plugins = [":sam_with_receiver_plugin"],
deps = [":runner"],
)
6 changes: 6 additions & 0 deletions examples/plugin/src/sam_with_receiver/Runner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package plugin.sam_with_receiver;

@SamWithReceiver
public interface Runner {
void run(Double shouldBecomeThis);
}
5 changes: 5 additions & 0 deletions examples/plugin/src/sam_with_receiver/RunnerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package plugin.sam_with_receiver

val thisShouldWork = Runner {
println(this.isFinite())
}
3 changes: 3 additions & 0 deletions examples/plugin/src/sam_with_receiver/SamWithReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package plugin.sam_with_receiver

annotation class SamWithReceiver
21 changes: 21 additions & 0 deletions kotlin/internal/compiler_plugins.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load(
"//kotlin/internal:defs.bzl",
_KtCompilerPluginInfo = "KtCompilerPluginInfo",
)

def plugins_to_classpaths(providers_list):
flattened_files = []
for providers in providers_list:
if _KtCompilerPluginInfo in providers:
provider = providers[_KtCompilerPluginInfo]
for e in provider.classpath:
flattened_files.append(e)
return flattened_files

def plugins_to_options(providers_list):
kt_compiler_plugin_providers = [providers[_KtCompilerPluginInfo] for providers in providers_list if _KtCompilerPluginInfo in providers]
flattened_options = []
for provider in kt_compiler_plugin_providers:
for option in provider.options:
flattened_options.append("%s:%s" % (option.id, option.value))
return flattened_options
7 changes: 7 additions & 0 deletions kotlin/internal/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ KtJsInfo = provider(
"srcjar": "The jar containing the sources of the library",
},
)

KtCompilerPluginInfo = provider(
fields = {
"classpath": "The kotlin compiler plugin classpath",
"options": "List of plugin options, represented as structs with an id and a value field, to be passed to the compiler",
},
)
29 changes: 24 additions & 5 deletions kotlin/internal/jvm/compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ load(
_merge_plugin_infos = "merge_plugin_infos",
_plugin_mappers = "mappers",
)
load(
"//kotlin/internal:compiler_plugins.bzl",
_plugins_to_classpaths = "plugins_to_classpaths",
_plugins_to_options = "plugins_to_options",
)
load(
"//kotlin/internal/utils:utils.bzl",
_utils = "utils",
Expand Down Expand Up @@ -218,8 +223,10 @@ def kt_jvm_compile_action(ctx, rule_kind, output_jar):
dirs = _compiler_directories(ctx)
srcs = _partitioned_srcs(ctx.files.srcs)
friend = _compiler_friends(ctx, friends = getattr(ctx.attr, "friends", []))
compile_deps = _compiler_deps(toolchains, friend, deps = ctx.attr.deps)
plugins = _plugin_mappers.targets_to_kt_plugins(ctx.attr.plugins + ctx.attr.deps)
compile_deps = _compiler_deps(toolchains, friend, deps = ctx.attr.deps + ctx.attr.plugins)
annotation_processors = _plugin_mappers.targets_to_annotation_processors(ctx.attr.plugins + ctx.attr.deps)
plugins = ctx.attr.plugins

_run_kt_builder_action(
ctx = ctx,
rule_kind = rule_kind,
Expand All @@ -228,6 +235,7 @@ def kt_jvm_compile_action(ctx, rule_kind, output_jar):
srcs = srcs,
friend = friend,
compile_deps = compile_deps,
annotation_processors = annotation_processors,
plugins = plugins,
outputs = {
"output": output_jar,
Expand Down Expand Up @@ -263,7 +271,7 @@ def kt_jvm_compile_action(ctx, rule_kind, output_jar):
),
)

def _run_kt_builder_action(ctx, rule_kind, toolchains, dirs, srcs, friend, compile_deps, plugins, outputs):
def _run_kt_builder_action(ctx, rule_kind, toolchains, dirs, srcs, friend, compile_deps, annotation_processors, plugins, outputs):
"""Creates a KotlinBuilder action invocation."""
args = _utils.init_args(ctx, rule_kind, friend.module_name)

Expand All @@ -279,17 +287,28 @@ def _run_kt_builder_action(ctx, rule_kind, toolchains, dirs, srcs, friend, compi
# Collect and prepare plugin descriptor for the worker.
args.add_all(
"--processors",
plugins,
annotation_processors,
map_each = _plugin_mappers.kt_plugin_to_processor,
omit_if_empty = True,
)
args.add_all(
"--processorpath",
plugins,
annotation_processors,
map_each = _plugin_mappers.kt_plugin_to_processorpath,
omit_if_empty = True,
)

args.add_all(
"--pluginpath",
_plugins_to_classpaths(plugins),
omit_if_empty = True,
)
args.add_all(
"--plugin_options",
_plugins_to_options(plugins),
omit_if_empty = True,
)

progress_message = "Compiling Kotlin to JVM %s { kt: %d, java: %d, srcjars: %d }" % (
ctx.label,
len(srcs.kt),
Expand Down
19 changes: 19 additions & 0 deletions kotlin/internal/jvm/impl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ load(
)
load(
"//kotlin/internal:defs.bzl",
_KtCompilerPluginInfo = "KtCompilerPluginInfo",
_KtJvmInfo = "KtJvmInfo",
)
load(
Expand Down Expand Up @@ -192,6 +193,7 @@ def kt_jvm_junit_test_impl(ctx):
main_class = ctx.attr.main_class,
jvm_flags = ["-ea", "-Dbazel.test_suite=%s" % test_class] + ctx.attr.jvm_flags,
)

return _make_providers(
ctx,
providers,
Expand All @@ -201,3 +203,20 @@ def kt_jvm_junit_test_impl(ctx):
direct = ctx.files._java_runtime,
),
)

def kt_compiler_plugin_impl(ctx):
merged_deps = java_common.merge([j[JavaInfo] for j in ctx.attr.deps])
plugin_id = ctx.attr.id
options = []
for (k, v) in ctx.attr.options.items():
if "=" in k:
fail("kt_compiler_plugin options keys cannot contain the = symbol")
options.append(struct(id = plugin_id, value = "%s=%s" % (k, v)))

return [
merged_deps,
_KtCompilerPluginInfo(
classpath = merged_deps.transitive_runtime_jars.to_list(),
options = options,
),
]
Loading