diff --git a/docs/kotlin.md b/docs/kotlin.md
index 534ac1c61..1ca735d2d 100755
--- a/docs/kotlin.md
+++ b/docs/kotlin.md
@@ -567,3 +567,21 @@ versions.use_repository(name, kwargs |
-
| none |
+
+
+## versions.get_major
+
+
+versions.get_major(version)
+
+
+
+
+**PARAMETERS**
+
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| version | -
| none |
+
+
diff --git a/kotlin/BUILD b/kotlin/BUILD
index e5919ea1b..6e04f5e26 100644
--- a/kotlin/BUILD
+++ b/kotlin/BUILD
@@ -54,13 +54,13 @@ release_archive(
genrule(
name = "stardoc",
- srcs = [doc for doc in [
- "js",
- "jvm",
- "lint",
- "core",
- "repositories.doc",
- ]],
+ srcs = [
+ ":js",
+ ":jvm",
+ ":lint",
+ ":core",
+ ":repositories.doc",
+ ],
outs = ["kotlin.md"],
cmd = """
for md in $(SRCS); do
diff --git a/src/main/kotlin/BUILD b/src/main/kotlin/BUILD
index db305906f..1d56e8d13 100644
--- a/src/main/kotlin/BUILD
+++ b/src/main/kotlin/BUILD
@@ -15,7 +15,7 @@ jar_jar(
jar_jar(
name = "jdeps-gen",
- input_jar = "//src/main/kotlin/io/bazel/kotlin/plugin/jdeps:jdeps-gen_deploy.jar",
+ input_jar = "//src/main/kotlin/io/bazel/kotlin/plugin:jdeps-gen_deploy.jar",
rules = "shade.jarjar",
visibility = ["//visibility:public"],
)
diff --git a/src/main/kotlin/bootstrap.bzl b/src/main/kotlin/bootstrap.bzl
index db7728d84..6a6bff9c9 100644
--- a/src/main/kotlin/bootstrap.bzl
+++ b/src/main/kotlin/bootstrap.bzl
@@ -30,7 +30,14 @@ def _resolve_dep_label(d):
else:
return d
-def kt_bootstrap_library(name, srcs, visibility = [], deps = [], neverlink_deps = [], runtime_deps = []):
+def kt_bootstrap_library(
+ name,
+ srcs,
+ visibility = [],
+ deps = [],
+ neverlink_deps = [],
+ runtime_deps = [],
+ kotlinc_repository_name = "com_github_jetbrains_kotlin"):
"""
Simple compilation of a kotlin library using a non-persistent worker. The target is a JavaInfo provider.
@@ -41,6 +48,7 @@ def kt_bootstrap_library(name, srcs, visibility = [], deps = [], neverlink_deps
deps: the dependenices, the are setup as runtime_deps of the library.
neverlink_deps: deps that won't be linked. These deps are added to the `"for_ide"` target.
"""
+ kotlinc_repository = "@" + kotlinc_repository_name
jar_label = name + "_jar"
dep_label = name + "_deps"
native.filegroup(
@@ -50,7 +58,7 @@ def kt_bootstrap_library(name, srcs, visibility = [], deps = [], neverlink_deps
visibility = ["//visibility:private"],
)
command = """
-function join_by { local IFS="$$1"; shift; echo "$$*"; }
+function join_by {{ local IFS="$$1"; shift; echo "$$*"; }}
case "$$(uname -s)" in
CYGWIN*|MINGW32*|MSYS*)
SEP=";"
@@ -59,14 +67,14 @@ case "$$(uname -s)" in
SEP=":"
;;
esac
-NAME=%s
-CP="%s"
-ARGS="%s"
+NAME={name}
+CP="{classpath}"
+ARGS="{args}"
CMD="$(JAVA) -Xmx256M -Xms32M -noverify \
- -cp $(location @com_github_jetbrains_kotlin//:kotlin-preloader) org.jetbrains.kotlin.preloading.Preloader \
- -cp $(location @com_github_jetbrains_kotlin//:kotlin-compiler) org.jetbrains.kotlin.cli.jvm.K2JVMCompiler \
- $$CP -d $(@D)/$${NAME}_temp.jar $${ARGS} $(SRCS)"
+ -cp $(location {kotlinc_repository}//:kotlin-preloader) org.jetbrains.kotlin.preloading.Preloader \
+ -cp $(location {kotlinc_repository}//:kotlin-compiler) org.jetbrains.kotlin.cli.jvm.K2JVMCompiler \
+ $$CP -d $(@D)/$${{NAME}}_temp.jar $${{ARGS}} $(SRCS)"
$$CMD
@@ -82,17 +90,22 @@ esac
$$SJ \
--normalize \
--compression \
- --sources $(@D)/$${NAME}_temp.jar \
+ --sources $(@D)/$${{NAME}}_temp.jar \
--output $(OUTS)
-rm $(@D)/$${NAME}_temp.jar
-""" % (name, "-cp $$(join_by $$SEP $(locations :%s)) " % dep_label if deps + neverlink_deps else "", " ".join(_BOOTSTRAP_LIB_ARGS))
+rm $(@D)/$${{NAME}}_temp.jar
+""".format(
+ name = name,
+ classpath = "-cp $$(join_by $$SEP $(locations :%s)) " % dep_label if deps + neverlink_deps else "",
+ args = " ".join(_BOOTSTRAP_LIB_ARGS),
+ kotlinc_repository = kotlinc_repository,
+ )
native.genrule(
name = jar_label,
tools = [
- "@com_github_jetbrains_kotlin//:home",
- "@com_github_jetbrains_kotlin//:kotlin-preloader",
- "@com_github_jetbrains_kotlin//:kotlin-compiler",
+ "%s//:home" % kotlinc_repository,
+ "%s//:kotlin-preloader" % kotlinc_repository,
+ "%s//:kotlin-compiler" % kotlinc_repository,
"@bazel_tools//tools/jdk:singlejar",
dep_label,
],
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/BUILD.bazel
index 4cda9aeeb..c1e1c58a9 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/BUILD.bazel
+++ b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/BUILD.bazel
@@ -27,8 +27,8 @@ kt_bootstrap_library(
visibility = ["//src:__subpackages__"],
deps = [
"//src/main/kotlin/io/bazel/kotlin/builder/utils",
+ "//src/main/kotlin/io/bazel/kotlin/plugin:jdeps-gen-lib",
"//src/main/kotlin/io/bazel/kotlin/plugin:skip-code-gen-lib",
- "//src/main/kotlin/io/bazel/kotlin/plugin/jdeps:jdeps-gen-lib",
"//src/main/protobuf:kotlin_model_java_proto",
"@com_github_jetbrains_kotlin//:kotlin-preloader",
"@kotlin_rules_maven//:com_google_protobuf_protobuf_java",
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/utils/jars/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/builder/utils/jars/BUILD.bazel
index a7157e50b..acc4a3cc0 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/utils/jars/BUILD.bazel
+++ b/src/main/kotlin/io/bazel/kotlin/builder/utils/jars/BUILD.bazel
@@ -1,3 +1,4 @@
+load("//src/main/starlark/core/repositories/kotlin:releases.bzl", "KOTLINC_INDEX")
load("//src/main/kotlin:bootstrap.bzl", "kt_bootstrap_library")
kt_bootstrap_library(
@@ -6,6 +7,8 @@ kt_bootstrap_library(
"*.kt",
"**/*.kt",
]),
+ # Need for the 1.6 jdeps-gen plugin
+ kotlinc_repository_name = KOTLINC_INDEX["1.6"].repository_name,
visibility = ["//src:__subpackages__"],
deps = [
"//src/main/protobuf:deps_java_proto",
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/plugin/BUILD.bazel
index eaa662124..7e424a8fa 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/BUILD.bazel
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/BUILD.bazel
@@ -11,35 +11,104 @@
# 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.
-
+load("//src/main/starlark/core/repositories/kotlin:releases.bzl", "KOTLINC_INDEX")
load("@rules_java//java:defs.bzl", "java_binary")
load("//src/main/kotlin:bootstrap.bzl", "kt_bootstrap_library")
load("//kotlin/internal/utils:generate_jvm_service.bzl", "generate_jvm_service")
+load("@com_github_jetbrains_kotlin//:version.bzl", KOTLINC_MAJOR_VERSION = "MAJOR_VERSION")
-# The compiler binary, this is co-located in the kotlin compiler classloader.
-kt_bootstrap_library(
- name = "skip-code-gen-lib",
- srcs = glob(["*.kt"]),
+# Generate a set of plugins for each major revision of the kotlinc compiler plugin api
+[
+ (
+ # jdeps generator plugin
+ kt_bootstrap_library(
+ name = "jdeps-gen-lib-%s" % version,
+ srcs = glob(["%s/jdeps/*.kt" % release.repository_name]),
+ kotlinc_repository_name = release.repository_name,
+ visibility = ["//src:__subpackages__"],
+ deps = [
+ "//src/main/kotlin/io/bazel/kotlin/builder/utils/jars",
+ "//src/main/protobuf:deps_java_proto",
+ "@%s//:kotlin-compiler" % release.repository_name,
+ "@kotlin_rules_maven//:com_google_protobuf_protobuf_java",
+ ],
+ ),
+
+ # services to integrate with the plugin.
+ generate_jvm_service(
+ name = "jdeps-gen-services-%s" % version,
+ services = {
+ "io.bazel.kotlin.plugin.%s.jdeps.JdepsGenComponentRegistrar" % release.repository_name: "org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar",
+ "io.bazel.kotlin.plugin.%s.jdeps.JdepsGenCommandLineProcessor" % release.repository_name: "org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor",
+ },
+ ),
+ # The plugin binary.
+ java_binary(
+ name = "jdeps-gen-%s" % version,
+ visibility = ["//src:__subpackages__"],
+ runtime_deps = [
+ ":jdeps-gen-lib-%s" % version,
+ ":jdeps-gen-services-%s" % version,
+ ],
+ ),
+ # SkipCodeGen utility plugin
+ kt_bootstrap_library(
+ name = "skip-code-gen-lib-%s" % version,
+ srcs = glob(["%s/*.kt" % release.repository_name]),
+ kotlinc_repository_name = release.repository_name,
+ visibility = ["//src:__subpackages__"],
+ deps = [
+ "@%s//:kotlin-compiler" % release.repository_name,
+ ],
+ ),
+ # services to integrate with the plugin.
+ generate_jvm_service(
+ name = "skip-code-gen-services-%s" % version,
+ services = {
+ "io.bazel.kotlin.plugin.%s.SkipCodeGen" % release.repository_name: "org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar",
+ },
+ ),
+ # The plugin binary.
+ java_binary(
+ name = "skip-code-gen-%s" % version,
+ visibility = ["//src:__subpackages__"],
+ runtime_deps = [
+ ":skip-code-gen-lib-%s" % version,
+ ":skip-code-gen-services-%s" % version,
+ ],
+ ),
+ )
+ for version, release in KOTLINC_INDEX.items()
+]
+
+# Deploy jars are not aliased, so repack in a java_binary.
+java_binary(
+ name = "skip-code-gen",
visibility = ["//src:__subpackages__"],
- deps = [
- "@com_github_jetbrains_kotlin//:kotlin-compiler",
+ runtime_deps = [
+ ":jdeps-gen-lib-%s" % KOTLINC_MAJOR_VERSION,
+ ":jdeps-gen-services-%s" % KOTLINC_MAJOR_VERSION,
],
)
-# services to integrate with the plugin.
-generate_jvm_service(
- name = "skip-code-gen-services",
- services = {
- "io.bazel.kotlin.plugin.SkipCodeGen": "org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar",
- },
+alias(
+ name = "skip-code-gen-lib",
+ actual = "skip-code-gen-lib-%s" % KOTLINC_MAJOR_VERSION,
+ visibility = ["//src:__subpackages__"],
)
-# The plugin binary.
+# Deploy jars are not aliased, so repack in a java_binary.
java_binary(
- name = "skip-code-gen",
+ name = "jdeps-gen",
visibility = ["//src:__subpackages__"],
runtime_deps = [
- ":skip-code-gen-lib",
- ":skip-code-gen-services",
+ ":jdeps-gen-lib-%s" % KOTLINC_MAJOR_VERSION,
+ ":jdeps-gen-services-%s" % KOTLINC_MAJOR_VERSION,
],
)
+
+alias(
+ name = "jdeps-gen-lib",
+ actual = "jdeps-gen-lib-%s" % KOTLINC_MAJOR_VERSION,
+ visibility = ["//src:__subpackages__"],
+)
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/SkipCodeGen.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/SkipCodeGen.kt
new file mode 100644
index 000000000..13e2f0ebe
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/SkipCodeGen.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 The Bazel Authors. 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.
+ *
+ */
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_6
+
+import com.google.common.base.Preconditions
+import com.intellij.mock.MockProject
+import com.intellij.openapi.project.Project
+import org.jetbrains.kotlin.analyzer.AnalysisResult
+import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.container.ComponentProvider
+import org.jetbrains.kotlin.context.ProjectContext
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+
+/**
+ * SkipCodeGen registers an extension to skip code generation. Must be the last compiler plugin.
+ */
+class SkipCodeGen : ComponentRegistrar {
+
+ companion object {
+ val COMPILER_PLUGIN_ID = "io.bazel.kotlin.plugin.kt_1_8.SkipCodeGen"
+ }
+
+ override fun registerProjectComponents(
+ project: MockProject,
+ configuration: CompilerConfiguration,
+ ) {
+ AnalysisHandlerExtension.registerExtension(
+ project,
+ SkipCodeGen,
+ )
+ }
+
+ /**
+ * SkipCodeGen ends the compilation
+ */
+ private object SkipCodeGen : AnalysisHandlerExtension {
+
+ override fun doAnalysis(
+ project: Project,
+ module: ModuleDescriptor,
+ projectContext: ProjectContext,
+ files: Collection,
+ bindingTrace: BindingTrace,
+ componentProvider: ComponentProvider,
+ ): AnalysisResult? {
+ return null
+ }
+
+ // analysisCompleted generates the module jvm abi and requests code generation to be skipped.
+ override fun analysisCompleted(
+ project: Project,
+ module: ModuleDescriptor,
+ bindingTrace: BindingTrace,
+ files: Collection,
+ ): AnalysisResult? {
+ // Ensure this is the last plugin, as it will short circuit any other plugin analysisCompleted
+ // calls.
+ Preconditions.checkState(
+ AnalysisHandlerExtension.getInstances(project).last() == this,
+ "SkipCodeGen must be the last plugin: ${AnalysisHandlerExtension.getInstances(project)}",
+ )
+ return AnalysisResult.Companion.success(bindingTrace.bindingContext, module, false)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenCommandLineProcessor.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenCommandLineProcessor.kt
new file mode 100644
index 000000000..327bdbf85
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenCommandLineProcessor.kt
@@ -0,0 +1,62 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_6.jdeps
+
+import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
+import org.jetbrains.kotlin.compiler.plugin.CliOption
+import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
+import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
+import org.jetbrains.kotlin.config.CompilerConfiguration
+
+class JdepsGenCommandLineProcessor : CommandLineProcessor {
+ companion object {
+ val COMPILER_PLUGIN_ID = "io.bazel.kotlin.plugin.jdeps.JDepsGen"
+
+ val OUTPUT_JDEPS_FILE_OPTION: CliOption =
+ CliOption("output", "", "Output path for generated jdeps", required = true)
+ val TARGET_LABEL_OPTION: CliOption =
+ CliOption("target_label", "", "Label of target being analyzed", required = true)
+ val DIRECT_DEPENDENCIES_OPTION: CliOption =
+ CliOption(
+ "direct_dependencies",
+ "",
+ "List of targets direct dependencies",
+ required = false,
+ allowMultipleOccurrences = true,
+ )
+ val STRICT_KOTLIN_DEPS_OPTION: CliOption =
+ CliOption("strict_kotlin_deps", "", "Report strict deps violations", required = true)
+ }
+
+ override val pluginId: String
+ get() = COMPILER_PLUGIN_ID
+ override val pluginOptions: Collection
+ get() = listOf(
+ OUTPUT_JDEPS_FILE_OPTION,
+ TARGET_LABEL_OPTION,
+ DIRECT_DEPENDENCIES_OPTION,
+ STRICT_KOTLIN_DEPS_OPTION,
+ )
+
+ override fun processOption(
+ option: AbstractCliOption,
+ value: String,
+ configuration: CompilerConfiguration,
+ ) {
+ when (option) {
+ OUTPUT_JDEPS_FILE_OPTION -> configuration.put(JdepsGenConfigurationKeys.OUTPUT_JDEPS, value)
+ TARGET_LABEL_OPTION -> configuration.put(JdepsGenConfigurationKeys.TARGET_LABEL, value)
+ DIRECT_DEPENDENCIES_OPTION -> configuration.appendList(
+ JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES,
+ value,
+ )
+
+ STRICT_KOTLIN_DEPS_OPTION -> configuration.put(
+ JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS,
+ value,
+ )
+
+ else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
+ }
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenComponentRegistrar.kt
similarity index 87%
rename from src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt
rename to src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenComponentRegistrar.kt
index e4d5b5641..55d450971 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenComponentRegistrar.kt
@@ -1,4 +1,6 @@
-package io.bazel.kotlin.plugin.jdeps
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_6.jdeps
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
@@ -6,7 +8,6 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
-@OptIn(org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi::class)
class JdepsGenComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenConfigurationKeys.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenConfigurationKeys.kt
similarity index 89%
rename from src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenConfigurationKeys.kt
rename to src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenConfigurationKeys.kt
index a4616da16..3dcfe70b4 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenConfigurationKeys.kt
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenConfigurationKeys.kt
@@ -1,4 +1,6 @@
-package io.bazel.kotlin.plugin.jdeps
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_6.jdeps
import org.jetbrains.kotlin.config.CompilerConfigurationKey
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenExtension.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenExtension.kt
new file mode 100644
index 000000000..6e08b42f7
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_6/jdeps/JdepsGenExtension.kt
@@ -0,0 +1,418 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_6.jdeps
+
+import com.google.devtools.build.lib.view.proto.Deps
+import com.intellij.mock.MockProject
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import io.bazel.kotlin.builder.utils.jars.JarOwner
+import org.jetbrains.kotlin.analyzer.AnalysisResult
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.ParameterDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.descriptors.SourceElement
+import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaField
+import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
+import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
+import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass
+import org.jetbrains.kotlin.platform.TargetPlatform
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.FunctionImportedFromObject
+import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
+import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
+import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
+import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
+import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.TypeConstructor
+import org.jetbrains.kotlin.types.typeUtil.supertypes
+import java.io.BufferedOutputStream
+import java.io.File
+import java.nio.file.Paths
+
+/**
+ * Kotlin compiler extension that tracks classes (and corresponding classpath jars) needed to
+ * compile current kotlin target. Tracked data should include all classes whose changes could
+ * affect target's compilation out : direct class dependencies (i.e external classes directly
+ * used), but also their superclass, interfaces, etc.
+ * The primary use of this extension is to improve Kotlin module compilation avoidance in build
+ * systems (like Buck).
+ *
+ * Tracking of classes is done with a Remapper, which exposes all object types used by the class
+ * bytecode being generated. Tracking of the ancestor classes is done via modules and class
+ * descriptors that got generated during analysis/resolve phase of Kotlin compilation.
+ *
+ * Note: annotation processors dependencies may need to be tracked separatly (and may not need
+ * per-class ABI change tracking)
+ *
+ * @param project the current compilation project
+ * @param configuration the current compilation configuration
+ */
+class JdepsGenExtension(
+ val project: MockProject,
+ val configuration: CompilerConfiguration,
+) :
+ AnalysisHandlerExtension, StorageComponentContainerContributor {
+
+ companion object {
+
+ /**
+ * Returns the path of the jar archive file corresponding to the provided descriptor.
+ *
+ * @descriptor the descriptor, typically obtained from compilation analyze phase
+ * @return the path corresponding to the JAR where this class was loaded from, or null.
+ */
+ fun getClassCanonicalPath(descriptor: DeclarationDescriptorWithSource): String? {
+ return when (val sourceElement: SourceElement = descriptor.source) {
+ is JavaSourceElement ->
+ if (sourceElement.javaElement is BinaryJavaClass) {
+ (sourceElement.javaElement as BinaryJavaClass).virtualFile!!.canonicalPath
+ } else if (sourceElement.javaElement is BinaryJavaField) {
+ val containingClass = (sourceElement.javaElement as BinaryJavaField).containingClass
+ if (containingClass is BinaryJavaClass) {
+ containingClass.virtualFile!!.canonicalPath
+ } else {
+ null
+ }
+ } else {
+ // Ignore Java source local to this module.
+ null
+ }
+
+ is KotlinJvmBinarySourceElement ->
+ (sourceElement.binaryClass as VirtualFileKotlinClass).file.canonicalPath
+
+ else -> null
+ }
+ }
+
+ fun getClassCanonicalPath(typeConstructor: TypeConstructor): String? {
+ return (typeConstructor.declarationDescriptor as? DeclarationDescriptorWithSource)?.let {
+ getClassCanonicalPath(
+ it,
+ )
+ }
+ }
+ }
+
+ private val explicitClassesCanonicalPaths = mutableSetOf()
+ private val implicitClassesCanonicalPaths = mutableSetOf()
+
+ override fun registerModuleComponents(
+ container: StorageComponentContainer,
+ platform: TargetPlatform,
+ moduleDescriptor: ModuleDescriptor,
+ ) {
+ container.useInstance(
+ ClasspathCollectingChecker(
+ explicitClassesCanonicalPaths,
+ implicitClassesCanonicalPaths,
+ ),
+ )
+ }
+
+ class ClasspathCollectingChecker(
+ private val explicitClassesCanonicalPaths: MutableSet,
+ private val implicitClassesCanonicalPaths: MutableSet,
+ ) : CallChecker, DeclarationChecker {
+
+ override fun check(
+ resolvedCall: ResolvedCall<*>,
+ reportOn: PsiElement,
+ context: CallCheckerContext,
+ ) {
+ when (val resultingDescriptor = resolvedCall.resultingDescriptor) {
+ is FunctionImportedFromObject -> {
+ collectTypeReferences(
+ (resolvedCall.resultingDescriptor as FunctionImportedFromObject)
+ .containingObject.defaultType,
+ )
+ }
+
+ is PropertyImportedFromObject -> {
+ collectTypeReferences(
+ (resolvedCall.resultingDescriptor as PropertyImportedFromObject)
+ .containingObject.defaultType,
+ )
+ }
+
+ is JavaMethodDescriptor -> {
+ getClassCanonicalPath(
+ (resultingDescriptor.containingDeclaration as ClassDescriptor).typeConstructor,
+ )?.let {
+ explicitClassesCanonicalPaths.add(
+ it,
+ )
+ }
+ }
+
+ is FunctionDescriptor -> {
+ resultingDescriptor.returnType?.let { addImplicitDep(it) }
+ resultingDescriptor.valueParameters.forEach { valueParameter ->
+ addImplicitDep(valueParameter.type)
+ }
+ val virtualFileClass =
+ resultingDescriptor.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass
+ ?: return
+ explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
+ }
+
+ is ParameterDescriptor -> {
+ getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ is FakeCallableDescriptorForObject -> {
+ collectTypeReferences(resultingDescriptor.type)
+ }
+
+ is JavaPropertyDescriptor -> {
+ getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ is PropertyDescriptor -> {
+ when (resultingDescriptor.containingDeclaration) {
+ is ClassDescriptor -> collectTypeReferences(
+ (resultingDescriptor.containingDeclaration as ClassDescriptor).defaultType,
+ )
+
+ else -> {
+ val virtualFileClass =
+ (resultingDescriptor).getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass
+ ?: return
+ explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
+ }
+ }
+ addImplicitDep(resultingDescriptor.type)
+ }
+
+ else -> return
+ }
+ }
+
+ override fun check(
+ declaration: KtDeclaration,
+ descriptor: DeclarationDescriptor,
+ context: DeclarationCheckerContext,
+ ) {
+ when (descriptor) {
+ is ClassDescriptor -> {
+ descriptor.typeConstructor.supertypes.forEach {
+ collectTypeReferences(it)
+ }
+ }
+
+ is FunctionDescriptor -> {
+ descriptor.returnType?.let { collectTypeReferences(it) }
+ descriptor.valueParameters.forEach { valueParameter ->
+ collectTypeReferences(valueParameter.type)
+ }
+ descriptor.annotations.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ }
+
+ is PropertyDescriptor -> {
+ collectTypeReferences(descriptor.type)
+ descriptor.annotations.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ descriptor.backingField?.annotations?.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ }
+
+ is LocalVariableDescriptor -> {
+ collectTypeReferences(descriptor.type)
+ }
+ }
+ }
+
+ private fun addImplicitDep(it: KotlinType) {
+ getClassCanonicalPath(it.constructor)?.let { implicitClassesCanonicalPaths.add(it) }
+ }
+
+ private fun addExplicitDep(it: KotlinType) {
+ getClassCanonicalPath(it.constructor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ /**
+ * Records direct and indirect references for a given type. Direct references are explicitly
+ * used in the code, e.g: a type declaration or a generic type declaration. Indirect references
+ * are other types required for compilation such as supertypes and interfaces of those explicit
+ * types.
+ */
+ private fun collectTypeReferences(kotlinType: KotlinType, collectSuperTypes: Boolean = true) {
+ addExplicitDep(kotlinType)
+
+ if (collectSuperTypes) {
+ kotlinType.supertypes().forEach {
+ addImplicitDep(it)
+ }
+ }
+
+ collectTypeArguments(kotlinType)
+ }
+
+ fun collectTypeArguments(
+ kotlinType: KotlinType,
+ visitedKotlinTypes: MutableSet = mutableSetOf(),
+ ) {
+ visitedKotlinTypes.add(kotlinType)
+ kotlinType.arguments.map { it.type }.forEach { typeArgument ->
+ addExplicitDep(typeArgument)
+ typeArgument.supertypes().forEach { addImplicitDep(it) }
+ if (!visitedKotlinTypes.contains(typeArgument)) {
+ collectTypeArguments(typeArgument, visitedKotlinTypes)
+ }
+ }
+ }
+ }
+
+ override fun analysisCompleted(
+ project: Project,
+ module: ModuleDescriptor,
+ bindingTrace: BindingTrace,
+ files: Collection,
+ ): AnalysisResult? {
+ val directDeps = configuration.getList(JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES)
+ val targetLabel = configuration.getNotNull(JdepsGenConfigurationKeys.TARGET_LABEL)
+ val explicitDeps = createDepsMap(explicitClassesCanonicalPaths)
+
+ doWriteJdeps(directDeps, targetLabel, explicitDeps)
+
+ doStrictDeps(configuration, targetLabel, directDeps, explicitDeps)
+
+ return super.analysisCompleted(project, module, bindingTrace, files)
+ }
+
+ /**
+ * Returns a map of jars to classes loaded from those jars.
+ */
+ private fun createDepsMap(classes: Set): Map> {
+ val jarsToClasses = mutableMapOf>()
+ classes.forEach {
+ val parts = it.split("!/")
+ val jarPath = parts[0]
+ if (jarPath.endsWith(".jar")) {
+ jarsToClasses.computeIfAbsent(jarPath) { ArrayList() }.add(parts[1])
+ }
+ }
+ return jarsToClasses
+ }
+
+ private fun doWriteJdeps(
+ directDeps: MutableList,
+ targetLabel: String,
+ explicitDeps: Map>,
+ ) {
+ val implicitDeps = createDepsMap(implicitClassesCanonicalPaths)
+
+ // Build and write out deps.proto
+ val jdepsOutput = configuration.getNotNull(JdepsGenConfigurationKeys.OUTPUT_JDEPS)
+
+ val rootBuilder = Deps.Dependencies.newBuilder()
+ rootBuilder.success = true
+ rootBuilder.ruleLabel = targetLabel
+
+ val unusedDeps = directDeps.subtract(explicitDeps.keys)
+ unusedDeps.forEach { jarPath ->
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.UNUSED
+ dependency.path = jarPath
+ rootBuilder.addDependency(dependency)
+ }
+
+ explicitDeps.forEach { (jarPath, _) ->
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.EXPLICIT
+ dependency.path = jarPath
+ rootBuilder.addDependency(dependency)
+ }
+
+ implicitDeps.keys.subtract(explicitDeps.keys).forEach {
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.IMPLICIT
+ dependency.path = it
+ rootBuilder.addDependency(dependency)
+ }
+
+ BufferedOutputStream(File(jdepsOutput).outputStream()).use {
+ it.write(rootBuilder.build().toByteArray())
+ }
+ }
+
+ private fun doStrictDeps(
+ compilerConfiguration: CompilerConfiguration,
+ targetLabel: String,
+ directDeps: MutableList,
+ explicitDeps: Map>,
+ ) {
+ when (compilerConfiguration.getNotNull(JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS)) {
+ "warn" -> checkStrictDeps(explicitDeps, directDeps, targetLabel)
+ "error" -> {
+ if (checkStrictDeps(
+ explicitDeps,
+ directDeps,
+ targetLabel,
+ )
+ ) {
+ error("Strict Deps Violations - please fix")
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints strict deps warnings and returns true if violations were found.
+ */
+ private fun checkStrictDeps(
+ result: Map>,
+ directDeps: List,
+ targetLabel: String,
+ ): Boolean {
+ val missingStrictDeps = result.keys
+ .filter { !directDeps.contains(it) }
+ .map { JarOwner.readJarOwnerFromManifest(Paths.get(it)).label }
+
+ if (missingStrictDeps.isNotEmpty()) {
+ val open = "\u001b[35m\u001b[1m"
+ val close = "\u001b[0m"
+ val command =
+ """
+ |$open ** Please add the following dependencies:$close ${
+ missingStrictDeps.joinToString(
+ " ",
+ )
+ } to $targetLabel
+ |$open ** You can use the following buildozer command:$close buildozer 'add deps ${
+ missingStrictDeps.joinToString(
+ " ",
+ )
+ }' $targetLabel
+ """.trimMargin()
+
+ println(command)
+ return true
+ }
+ return false
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/SkipCodeGen.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/SkipCodeGen.kt
new file mode 100644
index 000000000..e6c20746d
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/SkipCodeGen.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 The Bazel Authors. 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.
+ *
+ */
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_7
+
+import com.google.common.base.Preconditions
+import com.intellij.mock.MockProject
+import com.intellij.openapi.project.Project
+import org.jetbrains.kotlin.analyzer.AnalysisResult
+import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.container.ComponentProvider
+import org.jetbrains.kotlin.context.ProjectContext
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+
+/**
+ * SkipCodeGen registers an extension to skip code generation. Must be the last compiler plugin.
+ */
+class SkipCodeGen : ComponentRegistrar {
+
+ companion object {
+ val COMPILER_PLUGIN_ID = "io.bazel.kotlin.plugin.SkipCodeGen"
+ }
+
+ override fun registerProjectComponents(
+ project: MockProject,
+ configuration: CompilerConfiguration,
+ ) {
+ AnalysisHandlerExtension.registerExtension(
+ project,
+ SkipCodeGen,
+ )
+ }
+
+ /**
+ * SkipCodeGen ends the compilation
+ */
+ private object SkipCodeGen : AnalysisHandlerExtension {
+
+ override fun doAnalysis(
+ project: Project,
+ module: ModuleDescriptor,
+ projectContext: ProjectContext,
+ files: Collection,
+ bindingTrace: BindingTrace,
+ componentProvider: ComponentProvider,
+ ): AnalysisResult? {
+ return null
+ }
+
+ // analysisCompleted generates the module jvm abi and requests code generation to be skipped.
+ override fun analysisCompleted(
+ project: Project,
+ module: ModuleDescriptor,
+ bindingTrace: BindingTrace,
+ files: Collection,
+ ): AnalysisResult? {
+ // Ensure this is the last plugin, as it will short circuit any other plugin analysisCompleted
+ // calls.
+ Preconditions.checkState(
+ AnalysisHandlerExtension.getInstances(project).last() == this,
+ "SkipCodeGen must be the last plugin: ${AnalysisHandlerExtension.getInstances(project)}",
+ )
+ return AnalysisResult.Companion.success(bindingTrace.bindingContext, module, false)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenCommandLineProcessor.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenCommandLineProcessor.kt
new file mode 100644
index 000000000..778814b90
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenCommandLineProcessor.kt
@@ -0,0 +1,81 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_7.jdeps
+
+import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
+import org.jetbrains.kotlin.compiler.plugin.CliOption
+import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
+import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.config.CompilerConfigurationKey
+
+class JdepsGenCommandLineProcessor : CommandLineProcessor {
+ companion object {
+ val COMPILER_PLUGIN_ID = "io.bazel.kotlin.plugin.jdeps.JDepsGen"
+
+ val OUTPUT_JDEPS_FILE_OPTION: CliOption =
+ CliOption("output", "", "Output path for generated jdeps", required = true)
+ val TARGET_LABEL_OPTION: CliOption =
+ CliOption("target_label", "", "Label of target being analyzed", required = true)
+ val DIRECT_DEPENDENCIES_OPTION: CliOption =
+ CliOption(
+ "direct_dependencies",
+ "",
+ "List of targets direct dependencies",
+ required = false,
+ allowMultipleOccurrences = true,
+ )
+ val STRICT_KOTLIN_DEPS_OPTION: CliOption =
+ CliOption("strict_kotlin_deps", "", "Report strict deps violations", required = true)
+ }
+
+ override val pluginId: String
+ get() = COMPILER_PLUGIN_ID
+ override val pluginOptions: Collection
+ get() = listOf(
+ OUTPUT_JDEPS_FILE_OPTION,
+ TARGET_LABEL_OPTION,
+ DIRECT_DEPENDENCIES_OPTION,
+ STRICT_KOTLIN_DEPS_OPTION,
+ )
+
+ override fun processOption(
+ option: AbstractCliOption,
+ value: String,
+ configuration: CompilerConfiguration,
+ ) {
+ when (option) {
+ OUTPUT_JDEPS_FILE_OPTION -> configuration.put(JdepsGenConfigurationKeys.OUTPUT_JDEPS, value)
+ TARGET_LABEL_OPTION -> configuration.put(JdepsGenConfigurationKeys.TARGET_LABEL, value)
+ DIRECT_DEPENDENCIES_OPTION -> configuration.appendList(
+ JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES,
+ value,
+ )
+
+ STRICT_KOTLIN_DEPS_OPTION -> configuration.put(
+ JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS,
+ value,
+ )
+
+ else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
+ }
+ }
+
+ override fun CompilerConfiguration.appendList(
+ option: CompilerConfigurationKey>,
+ value: T,
+ ) {
+ val paths = getList(option).toMutableList()
+ paths.add(value)
+ put(option, paths)
+ }
+
+ override fun CompilerConfiguration.appendList(
+ option: CompilerConfigurationKey>,
+ values: List,
+ ) {
+ val paths = getList(option).toMutableList()
+ paths.addAll(values)
+ put(option, paths)
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenComponentRegistrar.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenComponentRegistrar.kt
new file mode 100644
index 000000000..f7dc6001e
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenComponentRegistrar.kt
@@ -0,0 +1,23 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_7.jdeps
+
+import com.intellij.mock.MockProject
+import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+
+class JdepsGenComponentRegistrar : ComponentRegistrar {
+
+ override fun registerProjectComponents(
+ project: MockProject,
+ configuration: CompilerConfiguration,
+ ) {
+ // Capture all types referenced by the compiler for this module and look up the jar from which
+ // they were loaded from
+ val extension = JdepsGenExtension(project, configuration)
+ AnalysisHandlerExtension.registerExtension(project, extension)
+ StorageComponentContainerContributor.registerExtension(project, extension)
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenConfigurationKeys.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenConfigurationKeys.kt
new file mode 100644
index 000000000..b3f0afe69
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenConfigurationKeys.kt
@@ -0,0 +1,37 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_7.jdeps
+
+import org.jetbrains.kotlin.config.CompilerConfigurationKey
+
+object JdepsGenConfigurationKeys {
+ /**
+ * Output path of generated Jdeps proto file.
+ */
+ val OUTPUT_JDEPS: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.OUTPUT_JDEPS_FILE_OPTION.description,
+ )
+
+ /**
+ * Label of the Bazel target being analyzed.
+ */
+ val TARGET_LABEL: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.TARGET_LABEL_OPTION.description)
+
+ /**
+ * Label of the Bazel target being analyzed.
+ */
+ val STRICT_KOTLIN_DEPS: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.STRICT_KOTLIN_DEPS_OPTION.description,
+ )
+
+ /**
+ * List of direct dependencies of the target.
+ */
+ val DIRECT_DEPENDENCIES: CompilerConfigurationKey> =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.DIRECT_DEPENDENCIES_OPTION.description,
+ )
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenExtension.kt
similarity index 99%
rename from src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt
rename to src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenExtension.kt
index 6c5b68011..f87b0f63e 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_7/jdeps/JdepsGenExtension.kt
@@ -1,4 +1,6 @@
-package io.bazel.kotlin.plugin.jdeps
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_7.jdeps
import com.google.devtools.build.lib.view.proto.Deps
import com.intellij.mock.MockProject
@@ -94,8 +96,10 @@ class JdepsGenExtension(
// Ignore Java source local to this module.
null
}
+
is KotlinJvmBinarySourceElement ->
(sourceElement.binaryClass as VirtualFileKotlinClass).file.canonicalPath
+
else -> null
}
}
@@ -136,14 +140,17 @@ class JdepsGenExtension(
is FunctionImportedFromObject -> {
collectTypeReferences(resultingDescriptor.containingObject.defaultType)
}
+
is PropertyImportedFromObject -> {
collectTypeReferences(resultingDescriptor.containingObject.defaultType)
}
+
is JavaMethodDescriptor -> {
getClassCanonicalPath(
(resultingDescriptor.containingDeclaration as ClassDescriptor).typeConstructor,
)?.let { explicitClassesCanonicalPaths.add(it) }
}
+
is FunctionDescriptor -> {
resultingDescriptor.returnType?.let { addImplicitDep(it) }
resultingDescriptor.valueParameters.forEach { valueParameter ->
@@ -154,20 +161,25 @@ class JdepsGenExtension(
?: return
explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
}
+
is ParameterDescriptor -> {
getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
}
+
is FakeCallableDescriptorForObject -> {
collectTypeReferences(resultingDescriptor.type)
}
+
is JavaPropertyDescriptor -> {
getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
}
+
is PropertyDescriptor -> {
when (resultingDescriptor.containingDeclaration) {
is ClassDescriptor -> collectTypeReferences(
(resultingDescriptor.containingDeclaration as ClassDescriptor).defaultType,
)
+
else -> {
val virtualFileClass =
(resultingDescriptor).getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass
@@ -177,6 +189,7 @@ class JdepsGenExtension(
}
addImplicitDep(resultingDescriptor.type)
}
+
else -> return
}
}
@@ -192,6 +205,7 @@ class JdepsGenExtension(
collectTypeReferences(it)
}
}
+
is FunctionDescriptor -> {
descriptor.returnType?.let { collectTypeReferences(it) }
descriptor.valueParameters.forEach { valueParameter ->
@@ -204,6 +218,7 @@ class JdepsGenExtension(
collectTypeReferences(it)
}
}
+
is PropertyDescriptor -> {
collectTypeReferences(descriptor.type)
descriptor.annotations.forEach { annotation ->
@@ -213,6 +228,7 @@ class JdepsGenExtension(
collectTypeReferences(annotation.type)
}
}
+
is LocalVariableDescriptor -> {
collectTypeReferences(descriptor.type)
}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/SkipCodeGen.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/SkipCodeGen.kt
similarity index 96%
rename from src/main/kotlin/io/bazel/kotlin/plugin/SkipCodeGen.kt
rename to src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/SkipCodeGen.kt
index f9eea10d1..1d3b78aad 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/SkipCodeGen.kt
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/SkipCodeGen.kt
@@ -14,7 +14,9 @@
* limitations under the License.
*
*/
-package io.bazel.kotlin.plugin
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_8
import com.google.common.base.Preconditions
import com.intellij.mock.MockProject
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenCommandLineProcessor.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenCommandLineProcessor.kt
similarity index 95%
rename from src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenCommandLineProcessor.kt
rename to src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenCommandLineProcessor.kt
index 122dab2a3..92ed015b8 100644
--- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenCommandLineProcessor.kt
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenCommandLineProcessor.kt
@@ -1,4 +1,6 @@
-package io.bazel.kotlin.plugin.jdeps
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_8.jdeps
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CliOption
@@ -50,10 +52,12 @@ class JdepsGenCommandLineProcessor : CommandLineProcessor {
JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES,
value,
)
+
STRICT_KOTLIN_DEPS_OPTION -> configuration.put(
JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS,
value,
)
+
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenComponentRegistrar.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenComponentRegistrar.kt
new file mode 100644
index 000000000..7a4b94a63
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenComponentRegistrar.kt
@@ -0,0 +1,21 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_8.jdeps
+
+import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+
+@OptIn(org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi::class)
+class JdepsGenComponentRegistrar : CompilerPluginRegistrar() {
+
+ override val supportsK2: Boolean = false
+ override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
+ // Capture all types referenced by the compiler for this module and look up the jar from which
+ // they were loaded from
+ val extension = JdepsGenExtension(configuration)
+ AnalysisHandlerExtension.registerExtension(extension)
+ StorageComponentContainerContributor.registerExtension(extension)
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenConfigurationKeys.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenConfigurationKeys.kt
new file mode 100644
index 000000000..74921063d
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenConfigurationKeys.kt
@@ -0,0 +1,37 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_8.jdeps
+
+import org.jetbrains.kotlin.config.CompilerConfigurationKey
+
+object JdepsGenConfigurationKeys {
+ /**
+ * Output path of generated Jdeps proto file.
+ */
+ val OUTPUT_JDEPS: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.OUTPUT_JDEPS_FILE_OPTION.description,
+ )
+
+ /**
+ * Label of the Bazel target being analyzed.
+ */
+ val TARGET_LABEL: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.TARGET_LABEL_OPTION.description)
+
+ /**
+ * Label of the Bazel target being analyzed.
+ */
+ val STRICT_KOTLIN_DEPS: CompilerConfigurationKey =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.STRICT_KOTLIN_DEPS_OPTION.description,
+ )
+
+ /**
+ * List of direct dependencies of the target.
+ */
+ val DIRECT_DEPENDENCIES: CompilerConfigurationKey> =
+ CompilerConfigurationKey.create(
+ JdepsGenCommandLineProcessor.DIRECT_DEPENDENCIES_OPTION.description,
+ )
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenExtension.kt b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenExtension.kt
new file mode 100644
index 000000000..10dc91f12
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/plugin/com_github_jetbrains_kotlin_1_8/jdeps/JdepsGenExtension.kt
@@ -0,0 +1,417 @@
+@file:Suppress("ktlint:standard:package-name")
+
+package io.bazel.kotlin.plugin.com_github_jetbrains_kotlin_1_8.jdeps
+
+import com.google.devtools.build.lib.view.proto.Deps
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import io.bazel.kotlin.builder.utils.jars.JarOwner
+import org.jetbrains.kotlin.analyzer.AnalysisResult
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.ParameterDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.descriptors.SourceElement
+import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
+import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaField
+import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
+import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
+import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass
+import org.jetbrains.kotlin.platform.TargetPlatform
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.FunctionImportedFromObject
+import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
+import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
+import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
+import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
+import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.TypeConstructor
+import org.jetbrains.kotlin.types.typeUtil.supertypes
+import java.io.BufferedOutputStream
+import java.io.File
+import java.nio.file.Paths
+
+/**
+ * Kotlin compiler extension that tracks classes (and corresponding classpath jars) needed to
+ * compile current kotlin target. Tracked data should include all classes whose changes could
+ * affect target's compilation out : direct class dependencies (i.e. external classes directly
+ * used), but also their superclass, interfaces, etc.
+ * The primary use of this extension is to improve Kotlin module compilation avoidance in build
+ * systems (like Buck).
+ *
+ * Tracking of classes and their ancestors is done via modules and class
+ * descriptors that got generated during analysis/resolve phase of Kotlin compilation.
+ *
+ * Note: annotation processors dependencies may need to be tracked separately (and may not need
+ * per-class ABI change tracking)
+ *
+ * @param project the current compilation project
+ * @param configuration the current compilation configuration
+ */
+class JdepsGenExtension(
+ val configuration: CompilerConfiguration,
+) :
+ AnalysisHandlerExtension, StorageComponentContainerContributor {
+
+ companion object {
+
+ /**
+ * Returns the path of the jar archive file corresponding to the provided descriptor.
+ *
+ * @descriptor the descriptor, typically obtained from compilation analyze phase
+ * @return the path corresponding to the JAR where this class was loaded from, or null.
+ */
+ fun getClassCanonicalPath(descriptor: DeclarationDescriptorWithSource): String? {
+ return when (val sourceElement: SourceElement = descriptor.source) {
+ is JavaSourceElement ->
+ if (sourceElement.javaElement is BinaryJavaClass) {
+ (sourceElement.javaElement as BinaryJavaClass).virtualFile.canonicalPath
+ } else if (sourceElement.javaElement is BinaryJavaField) {
+ val containingClass = (sourceElement.javaElement as BinaryJavaField).containingClass
+ if (containingClass is BinaryJavaClass) {
+ containingClass.virtualFile.canonicalPath
+ } else {
+ null
+ }
+ } else {
+ // Ignore Java source local to this module.
+ null
+ }
+
+ is KotlinJvmBinarySourceElement ->
+ (sourceElement.binaryClass as VirtualFileKotlinClass).file.canonicalPath
+
+ else -> null
+ }
+ }
+
+ fun getClassCanonicalPath(typeConstructor: TypeConstructor): String? {
+ return (typeConstructor.declarationDescriptor as? DeclarationDescriptorWithSource)?.let {
+ getClassCanonicalPath(
+ it,
+ )
+ }
+ }
+ }
+
+ private val explicitClassesCanonicalPaths = mutableSetOf()
+ private val implicitClassesCanonicalPaths = mutableSetOf()
+
+ override fun registerModuleComponents(
+ container: StorageComponentContainer,
+ platform: TargetPlatform,
+ moduleDescriptor: ModuleDescriptor,
+ ) {
+ container.useInstance(
+ ClasspathCollectingChecker(explicitClassesCanonicalPaths, implicitClassesCanonicalPaths),
+ )
+ }
+
+ class ClasspathCollectingChecker(
+ private val explicitClassesCanonicalPaths: MutableSet,
+ private val implicitClassesCanonicalPaths: MutableSet,
+ ) : CallChecker, DeclarationChecker {
+
+ override fun check(
+ resolvedCall: ResolvedCall<*>,
+ reportOn: PsiElement,
+ context: CallCheckerContext,
+ ) {
+ when (val resultingDescriptor = resolvedCall.resultingDescriptor) {
+ is FunctionImportedFromObject -> {
+ collectTypeReferences(resultingDescriptor.containingObject.defaultType)
+ }
+
+ is PropertyImportedFromObject -> {
+ collectTypeReferences(resultingDescriptor.containingObject.defaultType)
+ }
+
+ is JavaMethodDescriptor -> {
+ getClassCanonicalPath(
+ (resultingDescriptor.containingDeclaration as ClassDescriptor).typeConstructor,
+ )?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ is FunctionDescriptor -> {
+ resultingDescriptor.returnType?.let { addImplicitDep(it) }
+ resultingDescriptor.valueParameters.forEach { valueParameter ->
+ collectTypeReferences(valueParameter.type, isExplicit = false)
+ }
+ val virtualFileClass =
+ resultingDescriptor.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass
+ ?: return
+ explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
+ }
+
+ is ParameterDescriptor -> {
+ getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ is FakeCallableDescriptorForObject -> {
+ collectTypeReferences(resultingDescriptor.type)
+ }
+
+ is JavaPropertyDescriptor -> {
+ getClassCanonicalPath(resultingDescriptor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ is PropertyDescriptor -> {
+ when (resultingDescriptor.containingDeclaration) {
+ is ClassDescriptor -> collectTypeReferences(
+ (resultingDescriptor.containingDeclaration as ClassDescriptor).defaultType,
+ )
+
+ else -> {
+ val virtualFileClass =
+ (resultingDescriptor).getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass
+ ?: return
+ explicitClassesCanonicalPaths.add(virtualFileClass.file.path)
+ }
+ }
+ addImplicitDep(resultingDescriptor.type)
+ }
+
+ else -> return
+ }
+ }
+
+ override fun check(
+ declaration: KtDeclaration,
+ descriptor: DeclarationDescriptor,
+ context: DeclarationCheckerContext,
+ ) {
+ when (descriptor) {
+ is ClassDescriptor -> {
+ descriptor.typeConstructor.supertypes.forEach {
+ collectTypeReferences(it)
+ }
+ }
+
+ is FunctionDescriptor -> {
+ descriptor.returnType?.let { collectTypeReferences(it) }
+ descriptor.valueParameters.forEach { valueParameter ->
+ collectTypeReferences(valueParameter.type)
+ }
+ descriptor.annotations.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ descriptor.extensionReceiverParameter?.value?.type?.let {
+ collectTypeReferences(it)
+ }
+ }
+
+ is PropertyDescriptor -> {
+ collectTypeReferences(descriptor.type)
+ descriptor.annotations.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ descriptor.backingField?.annotations?.forEach { annotation ->
+ collectTypeReferences(annotation.type)
+ }
+ }
+
+ is LocalVariableDescriptor -> {
+ collectTypeReferences(descriptor.type)
+ }
+ }
+ }
+
+ private fun addImplicitDep(it: KotlinType) {
+ getClassCanonicalPath(it.constructor)?.let { implicitClassesCanonicalPaths.add(it) }
+ }
+
+ private fun addExplicitDep(it: KotlinType) {
+ getClassCanonicalPath(it.constructor)?.let { explicitClassesCanonicalPaths.add(it) }
+ }
+
+ /**
+ * Records direct and indirect references for a given type. Direct references are explicitly
+ * used in the code, e.g: a type declaration or a generic type declaration. Indirect references
+ * are other types required for compilation such as supertypes and interfaces of those explicit
+ * types.
+ */
+ private fun collectTypeReferences(
+ kotlinType: KotlinType,
+ isExplicit: Boolean = true,
+ ) {
+ if (isExplicit) {
+ addExplicitDep(kotlinType)
+ } else {
+ addImplicitDep(kotlinType)
+ }
+
+ kotlinType.supertypes().forEach {
+ addImplicitDep(it)
+ }
+
+ collectTypeArguments(kotlinType, isExplicit)
+ }
+
+ private fun collectTypeArguments(
+ kotlinType: KotlinType,
+ isExplicit: Boolean,
+ visitedKotlinTypes: MutableSet = mutableSetOf(),
+ ) {
+ visitedKotlinTypes.add(kotlinType)
+ kotlinType.arguments.map { it.type }.forEach { typeArgument ->
+ if (isExplicit) {
+ addExplicitDep(typeArgument)
+ } else {
+ addImplicitDep(typeArgument)
+ }
+ typeArgument.supertypes().forEach { addImplicitDep(it) }
+ if (!visitedKotlinTypes.contains(typeArgument)) {
+ collectTypeArguments(typeArgument, isExplicit, visitedKotlinTypes)
+ }
+ }
+ }
+ }
+
+ override fun analysisCompleted(
+ project: Project,
+ module: ModuleDescriptor,
+ bindingTrace: BindingTrace,
+ files: Collection,
+ ): AnalysisResult? {
+ val directDeps = configuration.getList(JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES)
+ val targetLabel = configuration.getNotNull(JdepsGenConfigurationKeys.TARGET_LABEL)
+ val explicitDeps = createDepsMap(explicitClassesCanonicalPaths)
+
+ doWriteJdeps(directDeps, targetLabel, explicitDeps)
+
+ doStrictDeps(configuration, targetLabel, directDeps, explicitDeps)
+
+ return super.analysisCompleted(project, module, bindingTrace, files)
+ }
+
+ /**
+ * Returns a map of jars to classes loaded from those jars.
+ */
+ private fun createDepsMap(classes: Set): Map> {
+ val jarsToClasses = mutableMapOf>()
+ classes.forEach {
+ val parts = it.split("!/")
+ val jarPath = parts[0]
+ if (jarPath.endsWith(".jar")) {
+ jarsToClasses.computeIfAbsent(jarPath) { ArrayList() }.add(parts[1])
+ }
+ }
+ return jarsToClasses
+ }
+
+ private fun doWriteJdeps(
+ directDeps: MutableList,
+ targetLabel: String,
+ explicitDeps: Map>,
+ ) {
+ val implicitDeps = createDepsMap(implicitClassesCanonicalPaths)
+
+ // Build and write out deps.proto
+ val jdepsOutput = configuration.getNotNull(JdepsGenConfigurationKeys.OUTPUT_JDEPS)
+
+ val rootBuilder = Deps.Dependencies.newBuilder()
+ rootBuilder.success = true
+ rootBuilder.ruleLabel = targetLabel
+
+ val unusedDeps = directDeps.subtract(explicitDeps.keys)
+ unusedDeps.forEach { jarPath ->
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.UNUSED
+ dependency.path = jarPath
+ rootBuilder.addDependency(dependency)
+ }
+
+ explicitDeps.forEach { (jarPath, _) ->
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.EXPLICIT
+ dependency.path = jarPath
+ rootBuilder.addDependency(dependency)
+ }
+
+ implicitDeps.keys.subtract(explicitDeps.keys).forEach {
+ val dependency = Deps.Dependency.newBuilder()
+ dependency.kind = Deps.Dependency.Kind.IMPLICIT
+ dependency.path = it
+ rootBuilder.addDependency(dependency)
+ }
+
+ BufferedOutputStream(File(jdepsOutput).outputStream()).use {
+ it.write(rootBuilder.build().toByteArray())
+ }
+ }
+
+ private fun doStrictDeps(
+ compilerConfiguration: CompilerConfiguration,
+ targetLabel: String,
+ directDeps: MutableList,
+ explicitDeps: Map>,
+ ) {
+ when (compilerConfiguration.getNotNull(JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS)) {
+ "warn" -> checkStrictDeps(explicitDeps, directDeps, targetLabel)
+ "error" -> {
+ if (checkStrictDeps(explicitDeps, directDeps, targetLabel)) {
+ error(
+ "Strict Deps Violations - please fix",
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints strict deps warnings and returns true if violations were found.
+ */
+ private fun checkStrictDeps(
+ result: Map>,
+ directDeps: List,
+ targetLabel: String,
+ ): Boolean {
+ val missingStrictDeps = result.keys
+ .filter { !directDeps.contains(it) }
+ .map { JarOwner.readJarOwnerFromManifest(Paths.get(it)) }
+
+ if (missingStrictDeps.isNotEmpty()) {
+ val missingStrictLabels = missingStrictDeps.mapNotNull { it.label }
+
+ val open = "\u001b[35m\u001b[1m"
+ val close = "\u001b[0m"
+
+ var command =
+ """
+ $open ** Please add the following dependencies:$close
+ ${
+ missingStrictDeps.map { it.label ?: it.jar }.joinToString(" ")
+ } to $targetLabel
+ """
+
+ if (missingStrictLabels.isNotEmpty()) {
+ command += """$open ** You can use the following buildozer command:$close
+ buildozer 'add deps ${
+ missingStrictLabels.joinToString(" ")
+ }' $targetLabel
+ """
+ }
+
+ println(command.trimIndent())
+ return true
+ }
+ return false
+ }
+}
diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel
deleted file mode 100644
index 60f5b07ee..000000000
--- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2020 The Bazel Authors. 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.
-
-load("@rules_java//java:defs.bzl", "java_binary")
-load("//src/main/kotlin:bootstrap.bzl", "kt_bootstrap_library")
-load("//kotlin/internal/utils:generate_jvm_service.bzl", "generate_jvm_service")
-
-# The compiler binary, this is co-located in the kotlin compiler classloader.
-kt_bootstrap_library(
- name = "jdeps-gen-lib",
- srcs = glob(["*.kt"]),
- visibility = ["//src:__subpackages__"],
- deps = [
- "//src/main/kotlin/io/bazel/kotlin/builder/utils/jars",
- "//src/main/protobuf:deps_java_proto",
- "@com_github_jetbrains_kotlin//:kotlin-compiler",
- "@kotlin_rules_maven//:com_google_protobuf_protobuf_java",
- ],
-)
-
-# services to integrate with the plugin.
-generate_jvm_service(
- name = "jdeps-gen-services",
- services = {
- "io.bazel.kotlin.plugin.jdeps.JdepsGenComponentRegistrar": "org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar",
- "io.bazel.kotlin.plugin.jdeps.JdepsGenCommandLineProcessor": "org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor",
- },
-)
-
-# The plugin binary.
-java_binary(
- name = "jdeps-gen",
- visibility = ["//src:__subpackages__"],
- runtime_deps = [
- ":jdeps-gen-lib",
- ":jdeps-gen-services",
- ],
-)
diff --git a/src/main/starlark/core/repositories/initialize.bzl b/src/main/starlark/core/repositories/initialize.bzl
index 902d6004e..f7d8b01c4 100644
--- a/src/main/starlark/core/repositories/initialize.bzl
+++ b/src/main/starlark/core/repositories/initialize.bzl
@@ -17,10 +17,13 @@
load(":setup.bzl", "kt_configure")
load(
":initialize.release.bzl",
+ _RULES_KOTLIN = "RULES_KOTLIN",
_kotlinc_version = "kotlinc_version",
_ksp_version = "ksp_version",
_release_kotlin_repositories = "kotlin_repositories",
)
+load("//src/main/starlark/core/repositories/kotlin:compiler.bzl", "kotlin_compiler_repository")
+load("//src/main/starlark/core/repositories/kotlin:releases.bzl", "KOTLINC_INDEX")
load(":versions.bzl", _versions = "versions")
#exports
@@ -39,3 +42,16 @@ def kotlin_repositories(
"""
_release_kotlin_repositories(compiler_release = compiler_release, ksp_compiler_release = ksp_compiler_release)
kt_configure()
+
+ # Provide versioned kotlinc repositories. These are used for compiling plugins.
+ for versioned_kotlinc in KOTLINC_INDEX.values():
+ kotlin_compiler_repository(
+ name = versioned_kotlinc.repository_name,
+ urls = [
+ url.format(version = versioned_kotlinc.release.version)
+ for url in versioned_kotlinc.release.url_templates
+ ],
+ sha256 = versioned_kotlinc.release.sha256,
+ kotlin_rules = _RULES_KOTLIN.workspace_name,
+ compiler_version = versioned_kotlinc.release.version,
+ )
diff --git a/src/main/starlark/core/repositories/initialize.release.bzl b/src/main/starlark/core/repositories/initialize.release.bzl
index dccdfb492..af9ab6ff4 100644
--- a/src/main/starlark/core/repositories/initialize.release.bzl
+++ b/src/main/starlark/core/repositories/initialize.release.bzl
@@ -27,7 +27,15 @@ load(
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
load("//src/main/starlark/core/repositories/kotlin:compiler.bzl", "kotlin_compiler_repository")
load(":ksp.bzl", "ksp_compiler_plugin_repository")
-load(":versions.bzl", "version", _versions = "versions")
+load(
+ ":versions.bzl",
+ _kotlinc_version = "kotlinc_version",
+ _ksp_version = "ksp_version",
+ _versions = "versions",
+)
+
+kotlinc_version = _kotlinc_version
+ksp_version = _ksp_version
versions = _versions
@@ -102,21 +110,3 @@ def kotlin_repositories(
urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/%s/bazel-skylib-%s.tar.gz" % (versions.SKYLIB_VERSION, versions.SKYLIB_VERSION)],
sha256 = versions.SKYLIB_SHA,
)
-
-def kotlinc_version(release, sha256):
- return version(
- version = release,
- url_templates = [
- "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip",
- ],
- sha256 = sha256,
- )
-
-def ksp_version(release, sha256):
- return version(
- version = release,
- url_templates = [
- "https://github.com/google/ksp/releases/download/{version}/artifacts.zip",
- ],
- sha256 = sha256,
- )
diff --git a/src/main/starlark/core/repositories/kotlin/compiler.bzl b/src/main/starlark/core/repositories/kotlin/compiler.bzl
index ab03814bb..9ff3ef9c8 100644
--- a/src/main/starlark/core/repositories/kotlin/compiler.bzl
+++ b/src/main/starlark/core/repositories/kotlin/compiler.bzl
@@ -1,3 +1,14 @@
+load("//src/main/starlark/core/repositories:versions.bzl", "versions")
+
+_CAPABILITIES_TEMPLATES = {
+ "legacy": "capabilities_legacy.bzl.com_github_jetbrains_kotlin.bazel", # keep first
+ "1.4": "capabilities_1.4.bzl.com_github_jetbrains_kotlin.bazel",
+ "1.5": "capabilities_1.5.bzl.com_github_jetbrains_kotlin.bazel",
+ "1.6": "capabilities_1.6.bzl.com_github_jetbrains_kotlin.bazel",
+ "1.7": "capabilities_1.7.bzl.com_github_jetbrains_kotlin.bazel",
+ "1.8": "capabilities_1.8.bzl.com_github_jetbrains_kotlin.bazel",
+}
+
def _kotlin_compiler_impl(repository_ctx):
"""Creates the kotlinc repository."""
attr = repository_ctx.attr
@@ -25,27 +36,28 @@ def _kotlin_compiler_impl(repository_ctx):
executable = False,
)
+ repository_ctx.file(
+ "version.bzl",
+ """
+ MAJOR_VERSION="%s"
+ """.strip() % versions.get_major(attr.compiler_version),
+ executable = False,
+ )
+
def _get_capability_template(compiler_version, templates):
+ major_version = versions.get_major(compiler_version)
+
for ver, template in zip(_CAPABILITIES_TEMPLATES.keys(), templates):
- if compiler_version.startswith(ver):
+ if ver == major_version:
return template
# After latest version
- if compiler_version > _CAPABILITIES_TEMPLATES.keys()[-1]:
+ if major_version > _CAPABILITIES_TEMPLATES.keys()[-1]:
templates[-1]
# Legacy
return templates[0]
-_CAPABILITIES_TEMPLATES = {
- "legacy": "capabilities_legacy.bzl.com_github_jetbrains_kotlin.bazel", # keep first
- "1.4": "capabilities_1.4.bzl.com_github_jetbrains_kotlin.bazel",
- "1.5": "capabilities_1.5.bzl.com_github_jetbrains_kotlin.bazel",
- "1.6": "capabilities_1.6.bzl.com_github_jetbrains_kotlin.bazel",
- "1.7": "capabilities_1.7.bzl.com_github_jetbrains_kotlin.bazel",
- "1.8": "capabilities_1.8.bzl.com_github_jetbrains_kotlin.bazel",
-}
-
kotlin_compiler_repository = repository_rule(
implementation = _kotlin_compiler_impl,
attrs = {
diff --git a/src/main/starlark/core/repositories/kotlin/releases.bzl b/src/main/starlark/core/repositories/kotlin/releases.bzl
new file mode 100644
index 000000000..73d9480e7
--- /dev/null
+++ b/src/main/starlark/core/repositories/kotlin/releases.bzl
@@ -0,0 +1,16 @@
+"""Kotlinc releases indexed by major versions."""
+
+load("//src/main/starlark/core/repositories:versions.bzl", "versions")
+
+# Index of major kotlinc revision to calculated repository name and release.
+KOTLINC_INDEX = {
+ major: struct(
+ # defining the expected repository name to reduce toil when updating.
+ repository_name = "com_github_jetbrains_kotlin_%s" % major.replace(".", "_"),
+ release = release,
+ )
+ for (major, release) in [
+ (versions.get_major(compiler_release.version), compiler_release)
+ for compiler_release in versions.KOTLIN_COMPILER_RELEASES
+ ]
+}
diff --git a/src/main/starlark/core/repositories/versions.bzl b/src/main/starlark/core/repositories/versions.bzl
index 5ee0999de..9c7347b79 100644
--- a/src/main/starlark/core/repositories/versions.bzl
+++ b/src/main/starlark/core/repositories/versions.bzl
@@ -10,6 +10,24 @@ version = provider(
},
)
+def kotlinc_version(release, sha256):
+ return version(
+ version = release,
+ url_templates = [
+ "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip",
+ ],
+ sha256 = sha256,
+ )
+
+def ksp_version(release, sha256):
+ return version(
+ version = release,
+ url_templates = [
+ "https://github.com/google/ksp/releases/download/{version}/artifacts.zip",
+ ],
+ sha256 = sha256,
+ )
+
def _use_repository(name, version, rule, **kwargs):
http_archive_arguments = dict(kwargs)
http_archive_arguments["sha256"] = version.sha256
@@ -19,6 +37,10 @@ def _use_repository(name, version, rule, **kwargs):
maybe(rule, name = name, **http_archive_arguments)
+def _get_major(version):
+ parts = version.split(".")
+ return ".".join(parts[:2])
+
versions = struct(
RULES_NODEJS_VERSION = "5.5.3",
RULES_NODEJS_SHA = "f10a3a12894fc3c9bf578ee5a5691769f6805c4be84359681a785a0c12e8d2b6",
@@ -61,13 +83,24 @@ versions = struct(
],
sha256 = "2b3f6f674a944d25bb8d283c3539947bbe86074793012909a55de4b771f74bcc",
),
- KOTLIN_CURRENT_COMPILER_RELEASE = version(
- version = "1.8.21",
- url_templates = [
- "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip",
- ],
+ KOTLIN_CURRENT_COMPILER_RELEASE = kotlinc_version(
+ release = "1.8.21",
sha256 = "6e43c5569ad067492d04d92c28cdf8095673699d81ce460bd7270443297e8fd7",
),
+ KOTLIN_COMPILER_RELEASES = [
+ kotlinc_version(
+ release = "1.8.21",
+ sha256 = "6e43c5569ad067492d04d92c28cdf8095673699d81ce460bd7270443297e8fd7",
+ ),
+ kotlinc_version(
+ release = "1.7.22",
+ sha256 = "9db4b467743c1aea8a21c08e1c286bc2aeb93f14c7ba2037dbd8f48adc357d83",
+ ),
+ kotlinc_version(
+ release = "1.6.21",
+ sha256 = "632166fed89f3f430482f5aa07f2e20b923b72ef688c8f5a7df3aa1502c6d8ba",
+ ),
+ ],
KSP_CURRENT_COMPILER_PLUGIN_RELEASE = version(
version = "1.8.21-1.0.11",
url_templates = [
@@ -111,4 +144,5 @@ versions = struct(
sha256 = None,
),
use_repository = _use_repository,
+ get_major = _get_major,
)