do not use _test as lib - test targets do not support this

GitOrigin-RevId: f2f49db4294b6a64040dbbd5f1d95e972cbd70a3
This commit is contained in:
Vladimir Krivosheev
2024-11-27 09:13:55 +01:00
committed by intellij-monorepo-bot
parent ee348c2a40
commit 98799639aa
997 changed files with 27414 additions and 19441 deletions

View File

@@ -1,5 +1,8 @@
# https://bazel.build/docs/bazel-and-java#hermetic-testing
# The code is compiled for, executed, and tested on this JVM.
build --@rules_jvm//:default-kotlinc-opts=//:k17
common --java_language_version=17
common --java_runtime_version=remotejdk_21

View File

@@ -1,3 +1,23 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_kotlin//kotlin:core.bzl", "define_kt_toolchain")
load("//build:compiler-options.bzl", "create_javac_options", "create_kotlinc_options")
create_javac_options(name = "j8", release = "8")
create_kotlinc_options(name= "k8", jvm_target="1.8")
create_javac_options(name = "j11", release = "11")
create_kotlinc_options(name= "k11", jvm_target= "11")
create_javac_options(name = "j17", release = "17")
create_kotlinc_options(name = "k17", jvm_target = "17")
define_kt_toolchain(
name = "kotlin_toolchain",
javac_options = ":j17",
)
java_binary(
name = "main_run",
runtime_deps = [":main"],
@@ -61,7 +81,6 @@ load("@rules_java//java:defs.bzl", "java_library")
java_library(
name = "main",
visibility = ["//visibility:public"],
exports = ["//java/ide-customization"],
runtime_deps = [
"//platform/main/intellij.platform.monolith.main:monolith-main",
"//plugins/coverage:java-coverage",
@@ -137,7 +156,7 @@ java_library(
"//plugins/java-i18n",
"//plugins/tasks/tasks-api:tasks",
"//plugins/tasks/tasks-java:java",
"//plugins/github:vcs-github",
"//plugins/github/github-core:vcs-github",
"//plugins/hg4idea:vcs-hg",
"//xml/relaxng",
"//plugins/gradle/java",
@@ -174,6 +193,7 @@ java_library(
"//plugins/gradle/gradle-dependency-updater:dependencyUpdater",
"//plugins/settings-sync:settingsSync",
"//plugins/settings-sync/git:settingsSync-git",
"//plugins/settings-sync/jba:settingsSync-jba",
"//java/java-features-trainer:featuresTrainer",
"//plugins/ml-local-models/java:ml-models-local-java",
"//plugins/kotlin:kotlin-plugin-community-main",
@@ -189,7 +209,6 @@ java_library(
"//plugins/keymaps/netbeans5.6-keymap:keymap-netbeans",
"//plugins/evaluation-plugin:evaluationPlugin",
"//plugins/evaluation-plugin/languages/java:evaluationPlugin-languages-java",
"//plugins/gitlab/gitlab-core:vcs-gitlab",
"//platform/warmup",
"//plugins/remote-control:remoteControl",
"//platform/webSymbols",
@@ -202,14 +221,17 @@ java_library(
"//plugins/kotlin/onboarding-promoter",
"//platform/execution-process-elevation",
"//plugins/github/community:vcs-github-community",
"//plugins/gitlab/gitlab-community:vcs-gitlab-community",
"//plugins/gitlab/gitlab-yaml:vcs-gitlab-yaml",
"//plugins/yaml/editing",
"//java/compiler/charts:java-compiler-charts",
"//platform/compose",
"//json/split",
"//java/java-terminal:terminal-tests",
"//java/java-terminal:terminal",
"//plugins/gitlab/gitlab-yaml:vcs-gitlab-yaml",
"//jvm/jvm-analysis-impl:analysis-impl",
"//plugins/ByteCodeViewer:java-byteCodeViewer",
"//plugins/github/github-json:vcs-github-json",
]
)
### auto-generated section `build intellij.idea.community.main` end

View File

@@ -1,14 +1,14 @@
module(name = "community")
bazel_dep(name = "rules_kotlin", version = "2.0.0-jb.6")
bazel_dep(name = "rules_java", version = "7.12.2")
bazel_dep(name = "rules_java", version = "8.6.2")
bazel_dep(name = "rules_jvm", version = "0.0.1")
bazel_dep(name = "lib")
local_path_override(
module_name = "lib",
path = "build/lib",
path = "lib",
)
local_path_override(
module_name = "rules_jvm",
@@ -17,13 +17,13 @@ local_path_override(
archive_override(
module_name = "rules_kotlin",
urls = ["https://github.com/develar/rules_kotlin/releases/download/v2.0.0-jb.6/rules_kotlin-v2.0.0-jb.6.tar.gz"],
integrity = "sha256-3iSAUhC6qzrLcunGz5oTO7VuY4q7Nc2Q/c9LG/b0I/c=",
urls = ["https://github.com/develar/rules_kotlin/releases/download/v2.0.0-jb.12/rules_kotlin-v2.0.0-jb.12.tar.gz"],
integrity = "sha256-jWDqcTgDWbeBqISkF4XFI/WbgeejFJVXMIDsv2oX/ww=",
)
# GraalVM
git_override(
module_name = "rules_graalvm",
remote = "https://github.com/develar/rules_graalvm",
commit = "b7fdb0472562bc0438f9386bfb7e0621081a3946",
commit = "9d12232f7798df7087952314600f1ee0f4e4f9f7",
)

View File

@@ -1,7 +1,5 @@
### auto-generated section `build intellij.regexp` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources", "jvm_test")
jvm_resources(
name = "regexp_resources",
@@ -13,9 +11,7 @@ jvm_library(
name = "regexp",
module_name = "intellij.regexp",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java", "gen/**/*.kt", "gen/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java", "gen/**/*.kt", "gen/**/*.java"], allow_empty = True),
deps = [
"//platform/lang-api:lang",
"//platform/analysis-impl",
@@ -24,33 +20,39 @@ jvm_library(
"//platform/lang-impl",
"//xml/xml-psi-api:psi",
"@lib//:fastutil-min",
"//platform/ide-core-impl",
"//platform/core-ui",
"//platform/util/jdom",
],
runtime_deps = [":regexp_resources"]
)
kt_jvm_test(
name = "regexp_test",
jvm_library(
name = "regexp_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True),
associates = [":regexp"],
deps = [
"//platform/lang-api:lang",
"//platform/analysis-impl",
"//platform/util:util-ui",
"//platform/platform-impl:ide-impl",
"//platform/platform-impl:ide-impl_test_lib",
"@lib//:junit4",
"//platform/lang-impl",
"//platform/testFramework",
"//platform/testFramework:testFramework_test",
"//platform/testFramework:testFramework_test_lib",
"//xml/xml-psi-api:psi",
"@lib//:fastutil-min",
"//platform/ide-core-impl",
"//platform/core-ui",
"//platform/util/jdom",
],
runtime_deps = [":regexp_resources"]
)
jvm_test(
name = "regexp_test",
runtime_deps = [":regexp_test_lib"]
)
### auto-generated section `build intellij.regexp` end

View File

@@ -1,14 +1,13 @@
### auto-generated section `build intellij.java.aetherDependencyResolver` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_test")
jvm_library(
name = "java-aetherDependencyResolver",
module_name = "intellij.java.aetherDependencyResolver",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j11",
kotlinc_opts = "@rules_jvm//:k11",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
javac_opts = "@community//:j11",
kotlinc_opts = "@community//:k11",
deps = [
"@lib//:jetbrains-annotations",
"@lib//:http-client",
@@ -33,12 +32,12 @@ jvm_library(
]
)
kt_jvm_test(
name = "java-aetherDependencyResolver_test",
jvm_library(
name = "java-aetherDependencyResolver_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["testSrc/**/*.kt", "testSrc/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j11",
kotlinc_opts = "@rules_jvm//:k11",
srcs = glob(["testSrc/**/*.kt", "testSrc/**/*.java"], allow_empty = True),
javac_opts = "@community//:j11",
kotlinc_opts = "@community//:k11",
associates = [":java-aetherDependencyResolver"],
deps = [
"@lib//:jetbrains-annotations",
@@ -47,7 +46,7 @@ kt_jvm_test(
"@lib//:slf4j-api",
"@lib//:slf4j-jdk14",
"//platform/testFramework",
"//platform/testFramework:testFramework_test",
"//platform/testFramework:testFramework_test_lib",
"@lib//:maven-resolver-provider",
"@lib//:java_aether_dependency_resolver_org_apache_maven_resolver_maven-resolver-transport-file",
"@lib//:java_aether_dependency_resolver_org_apache_maven_resolver_maven-resolver-transport-http",
@@ -58,4 +57,9 @@ kt_jvm_test(
"//platform/util",
]
)
jvm_test(
name = "java-aetherDependencyResolver_test",
runtime_deps = [":java-aetherDependencyResolver_test_lib"]
)
### auto-generated section `build intellij.java.aetherDependencyResolver` end

View File

@@ -1,13 +1,11 @@
### auto-generated section `build intellij.idea.community.build` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "build",
module_name = "intellij.idea.community.build",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//platform/build-scripts:buildScripts",
"@lib//:kotlin-stdlib",

View File

@@ -1,5 +1,5 @@
load("@rules_kotlin//kotlin:jvm.bzl", "kt_javac_options")
load("@rules_kotlin//kotlin:core.bzl", "kt_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "kt_kotlinc_options")
def create_javac_options(name, release):
kt_javac_options(
@@ -13,20 +13,15 @@ def create_kotlinc_options(name, jvm_target, opt_in = [], allow_kotlin_package =
kt_kotlinc_options(
name = name,
jvm_target = jvm_target,
x_enable_incremental_compilation = True,
x_optin = [
opt_in = [
"com.intellij.openapi.util.IntellijInternalApi",
# it is unusual to have such opt-ins for the entire monorepo,
# but that is what JPS uses as the default - in bazel, let's not use it for all
"org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction",
"org.jetbrains.kotlin.analysis.api.KaIdeApi",
"org.jetbrains.kotlin.analysis.api.KaNonPublicApi",
] + opt_in,
x_jvm_default = "all",
x_lambdas = "indy",
jvm_default = "all",
warn = "off",
visibility=["//visibility:public"],
include_stdlibs = "none",
x_allow_kotlin_package = allow_kotlin_package,
x_context_receivers = context_receivers,
allow_kotlin_package = allow_kotlin_package,
context_receivers = context_receivers,
)

View File

@@ -1,16 +1,14 @@
### auto-generated section `build intellij.cucumber.testRunner` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "cucumber-testRunner",
module_name = "intellij.cucumber.testRunner",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//platform/testFramework",
"@lib//:cucumber-core.provided",
"@lib//:cucumber-core-1-provided",
"//platform/util-class-loader:util-classLoader",
],
runtime_deps = ["//plugins/cucumber-jvm-formatter:cucumber-jvmFormatter"]

View File

@@ -1,13 +1,11 @@
### auto-generated section `build intellij.idea.community.build.dependencies` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "dependencies",
module_name = "intellij.idea.community.build.dependencies",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"@lib//:jetbrains-annotations",
"//platform/build-scripts/downloader:buildScripts-downloader",

View File

@@ -12,6 +12,7 @@ build --tool_java_runtime_version=remotejdk_21
# make sure you don't need to open file to read commpilation errors
common --experimental_ui_max_stdouterr_bytes=-1
common --symlink_prefix=/
# disable deprecated WORKSPACE support
# common --noenable_workspace

View File

@@ -1,103 +1,45 @@
load("@contrib_rules_jvm//docs:stardoc-input.bzl", "java_junit5_test")
load("@rules_graalvm//graalvm:defs.bzl", "native_image")
load("@rules_java//java:java_library.bzl", "java_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_binary", "kt_jvm_import", "kt_jvm_library")
load("@rules_java//java:defs.bzl", "java_import")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_import", "kt_jvm_library")
load("//:jvm.bzl", "jvm_import", "kt_kotlinc_options")
load("@rules_kotlin//kotlin:core.bzl", "define_kt_toolchain")
load(":compiler-options.bzl", "create_javac_options", "create_kotlinc_options")
define_kt_toolchain(
name = "kotlin_toolchain",
api_version = "2.0",
language_version = "2.0",
experimental_multiplex_workers = True,
# https://github.com/bazelbuild/rules_kotlin/issues/1233
# experimental_use_abi_jars = True,
)
create_javac_options(name = "j8", release = "8")
create_kotlinc_options(name="k8", jvm_target="1.8")
create_javac_options(name = "j11", release = "11")
create_kotlinc_options(name= "k11", jvm_target= "11")
create_javac_options(name = "j17", release = "17")
create_kotlinc_options(name = "k17", jvm_target = "17")
java_proto_library(
name = "worker_protocol_java_proto",
deps = ["@bazel_worker_api//:worker_protocol_proto"],
)
kt_jvm_import(
java_import(
name = "protobuf-java",
jar = "@protobuf-java-file//file",
jars = ["@protobuf-java-file//file"],
visibility = ["//visibility:public"],
)
kt_jvm_import(
java_import(
name = "protobuf-java-util",
jar = "@protobuf-java-util-file//file",
jars = ["@protobuf-java-util-file//file"],
visibility = ["//visibility:public"],
)
kt_jvm_library(
name = "worker",
srcs = glob(["src/*.kt"]),
deps = [
"//:worker_protocol_java_proto",
"//zip:build-zip",
":protobuf-java",
":protobuf-java-util",
],
)
kt_jvm_binary(
name = "worker-jvm",
runtime_deps = [":worker"],
main_class = "org.jetbrains.bazel.jvm.JvmWorker",
jvm_flags = [
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
]
)
native_image(
name = "worker-native",
deps = [":worker"],
extra_args = [
"-H:+UnlockExperimentalVMOptions",
"-H:+CompactingOldGen",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
"-march=native",
"-O3",
],
reflection_configuration = ":reflection-config.json",
main_class = "org.jetbrains.bazel.jvm.JvmWorker",
native_image_tool = "@graalvm//:native-image",
)
kt_jvm_library(
name = "worker_test_lib",
srcs = ["testSrc/WorkRequestHandlerTest.kt"],
associates = [":worker"],
deps = [
"//:worker_protocol_java_proto",
"@junit_jupiter_api//jar",
"@assertj//jar",
],
jvm_import(
name = "kotlin-compiler",
jar = "@kotlin-compiler_http//file",
source_jar = "@kotlin-compiler-sources_http//file",
visibility = ["//visibility:public"],
runtime_deps = [
"@junit_platform_commons//jar",
"@opentest4j//jar",
"@junit_jupiter_engine//jar",
"@junit_platform_engine//jar",
"@junit_platform_reporting//jar",
"@junit_platform_launcher//jar",
"@lib//:jetbrains-annotations",
"@lib//:kotlinx-coroutines-core",
"@lib//:platform_util_trove_trove",
],
)
java_junit5_test(
name = "worker_test",
test_class = "org.jetbrains.bazel.jvm.WorkRequestHandlerTest",
runtime_deps = [":worker_test_lib"],
jvm_import(
name = "kotlin-metadata-jvm",
jar = "@kotlin-metadata-jvm_http//file",
source_jar = "@kotlin-metadata-jvm-sources_http//file",
visibility = ["//visibility:public"],
)
label_flag(
name = "default-kotlinc-opts",
build_setting_default = ":default-kotlinc-opts-value",
visibility = ["//visibility:public"],
)
kt_kotlinc_options(
name = "default-kotlinc-opts-value",
visibility = ["//visibility:public"],
)

View File

@@ -1,25 +1,29 @@
module(name = "rules_jvm", version = "0.1")
bazel_dep(name = "bazel_worker_api", version = "0.0.4")
bazel_dep(name = "rules_java", version = "7.12.2")
bazel_dep(name = "rules_java", version = "8.6.2")
bazel_dep(name = "rules_kotlin", version = "2.0.0-jb.5")
bazel_dep(name = "rules_graalvm", version = "0.11.3")
bazel_dep(name = "lib")
bazel_dep(name = "protobuf", version = "29.1")
# https://github.com/bazelbuild/bazel/issues/6681#issuecomment-2188972754
bazel_dep(name = "contrib_rules_jvm", version = "0.27.0")
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
local_path_override(
module_name = "lib",
path = "../lib",
module_name = "lib",
path = "../../lib",
)
# GraalVM
git_override(
module_name = "rules_graalvm",
remote = "https://github.com/develar/rules_graalvm",
commit = "b7fdb0472562bc0438f9386bfb7e0621081a3946",
commit = "9d12232f7798df7087952314600f1ee0f4e4f9f7",
)
# local_path_override(
# module_name = "rules_graalvm",
# path = "../rules_graalvm",
@@ -40,64 +44,112 @@ register_toolchains("@graalvm//:toolchain")
# Kotlin Rules
archive_override(
module_name = "rules_kotlin",
urls = ["https://github.com/develar/rules_kotlin/releases/download/v2.0.0-jb.6/rules_kotlin-v2.0.0-jb.6.tar.gz"],
integrity = "sha256-3iSAUhC6qzrLcunGz5oTO7VuY4q7Nc2Q/c9LG/b0I/c=",
module_name = "rules_kotlin",
urls = ["https://github.com/develar/rules_kotlin/releases/download/v2.0.0-jb.12/rules_kotlin-v2.0.0-jb.12.tar.gz"],
integrity = "sha256-jWDqcTgDWbeBqISkF4XFI/WbgeejFJVXMIDsv2oX/ww=",
)
# Libraries
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
http_jar = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")
http_file(
name = "kotlin-compiler_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-compiler/2.1.0/kotlin-compiler-2.1.0.jar",
sha256 = "f6838d76c4a65da83e446ec7091b3f36195a90043010c67c79ddac0b3c8be08e",
downloaded_file_path = "kotlin-compiler-2.1.0.jar",
)
http_file(
name = "kotlin-compiler-sources_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-compiler/2.1.0/kotlin-compiler-2.1.0-sources.jar",
sha256 = "dd966fb4fd739f810c5ddf780fadbb4117f2fd029253ba363306af2bf698f9e9",
downloaded_file_path = "kotlin-compiler-2.1.0-sources.jar",
)
http_file(
name = "kotlin-build-tools-impl_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-build-tools-impl/2.1.0/kotlin-build-tools-impl-2.1.0.jar",
sha256 = "958827bc3711fc3c3491fc77f367a999ad558352b1a2b32d18f5916cce15ed39",
downloaded_file_path = "kotlin-build-tools-impl-sources.jar",
)
http_file(
name = "kotlin-build-tools-impl-sources_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-build-tools-impl/2.1.0/kotlin-build-tools-impl-2.1.0-sources.jar",
sha256 = "631013f06b985934e113dc306fc1bf01b9686c0453de475b553714b25c66fe02",
downloaded_file_path = "kotlin-build-tools-impl-2.1.0-sources.jar",
)
http_file(
name = "kotlin-metadata-jvm_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-metadata-jvm/2.1.0/kotlin-metadata-jvm-2.1.0.jar",
downloaded_file_path = "kotlin-metadata-jvm-2.1.0.jar",
integrity = "sha256-uNOpJXS6HfxJvfIFJW0e3gZkFIyxUKti+qhyteG7RjI=",
)
http_file(
name = "kotlin-metadata-jvm-sources_http",
url = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-metadata-jvm/2.1.0/kotlin-metadata-jvm-2.1.0-sources.jar",
downloaded_file_path = "kotlin-metadata-jvm-2.1.0-sources.jar",
integrity = "sha256-L8es+9WcPIu4fPyGXOelQcwupXtj9W7AZ4SzLBtXLvQ=",
)
http_file(
name = "protobuf-java-file",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/4.28.3/protobuf-java-4.28.3.jar",
sha256 = "ba02977c0fef8b40af9f85fe69af362d8e13f2685b49a9752750b18da726157e",
downloaded_file_path = "file.jar",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/4.29.1/protobuf-java-4.29.1.jar",
sha256 = "c7dc027ca23bca4d56a330c8048e4829d0d9631e745a64fde4d49c3a9341e68b",
downloaded_file_path = "protobuf-java-4.29.1.jar",
)
http_file(
name = "protobuf-java-util-file",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/4.28.3/protobuf-java-util-4.28.3.jar",
sha256 = "d706eb7b4fbb63b7a39891106341080ebbadbb78dd8daecba7a6e68d2831c9cd",
downloaded_file_path = "file.jar",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/4.29.1/protobuf-java-util-4.29.1.jar",
sha256 = "77862bfb6486a344beb7f369a541180bab0abbe833519765777a8b14d296b9c1",
downloaded_file_path = "protobuf-java-util-4.29.1.jar",
)
# Test Libraries
http_jar(
name = "junit_jupiter_api",
url = "https://repo1.maven.org/maven2/org/junit/jupiter/junit-jupiter-api/5.11.3/junit-jupiter-api-5.11.3.jar",
integrity = "sha256-XYFHpg9JRTlz4lDtaHAbf/BVlk/iRi/Cyx7B1tRIibo=",
)
http_jar(
name = "junit_jupiter_engine",
url = "https://repo1.maven.org/maven2/org/junit/jupiter/junit-jupiter-engine/5.11.3/junit-jupiter-engine-5.11.3.jar",
integrity = "sha256-5iQgyZ98DVmiFZou9j5hh36cgL1yLAPKi/O9zqBQpYk=",
)
http_jar(
name = "junit_platform_engine",
url = "https://repo1.maven.org/maven2/org/junit/platform/junit-platform-engine/1.11.3/junit-platform-engine-1.11.3.jar",
integrity = "sha256-AEP3L2EWZHNdqNyaMIvxLs0iNrBTOTUcR0HttNj6sNo=",
)
http_jar(
name = "junit_platform_reporting",
url = "https://repo1.maven.org/maven2/org/junit/platform/junit-platform-reporting/1.11.3/junit-platform-reporting-1.11.3.jar",
integrity = "sha256-uOGdvryufR/zC512cEf782lAJ8M9+kI7NxaTt/ZnntE=",
)
http_jar(
name = "junit_platform_launcher",
url = "https://repo1.maven.org/maven2/org/junit/platform/junit-platform-launcher/1.11.3/junit-platform-launcher-1.11.3.jar",
integrity = "sha256-tHJ0WSAbABG+sHQr2AdCGh/IQmsRYZMDHth4JbwtTwQ=",
)
http_jar(
name = "junit_platform_commons",
url = "https://repo1.maven.org/maven2/org/junit/platform/junit-platform-commons/1.11.3/junit-platform-commons-1.11.3.jar",
integrity = "sha256-viYpZLC2tI3pd8YdT5Md+M9h6A51DMPzoKOc3SHBAIw=",
)
http_jar(
name = "assertj",
url = "https://repo1.maven.org/maven2/org/assertj/assertj-core/3.26.3/assertj-core-3.26.3.jar",
integrity = "sha256-TC+GQY/0fua2f7xq2xlOgCGbeTKBs72ih5nUQlvJoL0=",
)
http_jar(
name = "opentest4j",
url = "https://repo1.maven.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar",
integrity = "sha256-SOLfY2yrZWPO1k3N/4q7I1VifLI27wvzdZhoLd90Lxs=",
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,63 +1,15 @@
load("@rules_java//java:defs.bzl", "JavaInfo", "java_common")
load("//:rules/library.bzl", _jvm_library = "jvm_library")
load("//:rules/provided-library.bzl", _jvm_provided_library = "jvm_provided_library")
load("//:rules/import.bzl", _jvm_import = "jvm_import")
load("//:rules/resource.bzl", _jvm_resources = "jvm_resources")
load("//:rules/test.bzl", _jvm_test = "jvm_test")
load("//:rules/impl/kotlinc-options.bzl", _kt_kotlinc_options = "kt_kotlinc_options")
def _jvm_resources_impl(ctx):
resultJar = ctx.actions.declare_file(ctx.label.name + ".jar")
jvm_resources = _jvm_resources
jvm_library = _jvm_library
jvm_test = _jvm_test
# args = ctx.actions.args()
# args.use_param_file("--flagfile=%s", use_always = True)
# args.add("jar")
# args.add(resultJar)
jvm_provided_library = _jvm_provided_library
jvm_import = _jvm_import
ctx.actions.run(
mnemonic = "PackageResources",
inputs = ctx.files.files,
# avoid creating small files on disk trick Bazel using this workaround
arguments = ["--flagfile=|jar|" + resultJar.path + "|" + ctx.file.strip_prefix.path],
# arguments = [args],
outputs = [resultJar],
executable = ctx.executable._worker,
execution_requirements = {
"supports-workers": "1",
"supports-multiplex-workers": "1",
"supports-worker-cancellation": "1",
},
env = {
# for Java source files
"LC_CTYPE": "en_US.UTF-8",
}
)
return [
DefaultInfo(
files = depset([resultJar]),
),
JavaInfo(
output_jar = resultJar,
# resource jar - should not be added as a compile-time dependency
compile_jar = None,
),
]
jvm_resources = rule(
doc = """This rule packages resources into a .jar file.""",
implementation = _jvm_resources_impl,
attrs = {
"files": attr.label_list(
doc = """The list of resource files to create the target""",
allow_files = True,
mandatory = True,
),
"strip_prefix": attr.label(
doc = """The path prefix to strip from Java resources""",
allow_single_file = True,
),
# see https://bazel.build/extending/rules#private_attributes_and_implicit_dependencies about implicit dependencies
"_worker": attr.label(
default = Label(":worker-native"),
# default = Label(":worker-jvm"),
executable = True,
allow_files = True,
cfg = "exec",
),
},
provides = [JavaInfo],
)
kt_kotlinc_options = _kt_kotlinc_options

View File

@@ -1,8 +0,0 @@
load(
"@rules_kotlin//kotlin:jvm.bzl",
_jvm_resources = "jvm_resources",
_kt_jvm_library = "kt_jvm_library",
)
jvm_resources = _jvm_resources
jvm_library = _kt_jvm_library

View File

@@ -0,0 +1,136 @@
load("@rules_java//java:defs.bzl", "JavaInfo")
load(
"@rules_kotlin//kotlin/internal:defs.bzl",
_JAVA_TOOLCHAIN_TYPE = "JAVA_TOOLCHAIN_TYPE",
_KtCompilerPluginInfo = "KtCompilerPluginInfo",
_KtJvmInfo = "KtJvmInfo",
_KtPluginConfiguration = "KtPluginConfiguration",
_TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE",
)
load(
"@rules_kotlin//kotlin/internal:opts.bzl",
_JavacOptions = "JavacOptions",
)
load(
"//:rules/impl/kotlinc-options.bzl",
"KotlincOptions",
)
visibility("private")
common_toolchains = [
_TOOLCHAIN_TYPE,
_JAVA_TOOLCHAIN_TYPE,
]
def add_dicts(*dictionaries):
result = {}
for d in dictionaries:
result.update(d)
return result
_implicit_deps = {
"_singlejar": attr.label(
executable = True,
cfg = "exec",
default = Label("@bazel_tools//tools/jdk:singlejar"),
allow_files = True,
),
"_zipper": attr.label(
executable = True,
cfg = "exec",
default = Label("@bazel_tools//tools/zip:zipper"),
allow_files = True,
),
"_java_stub_template": attr.label(
cfg = "exec",
default = Label("@bazel_tools//tools/java:java_stub_template.txt"),
allow_single_file = True,
),
"_java_toolchain": attr.label(
default = Label("@bazel_tools//tools/jdk:current_java_toolchain"),
),
"_host_javabase": attr.label(
default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
cfg = "exec",
),
}
common_attr = add_dicts(
_implicit_deps,
{
"deps": attr.label_list(
doc = """A list of dependencies of this rule.See general comments about `deps` at
[Attributes common to all build rules](https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes).""",
providers = [
[JavaInfo],
[_KtJvmInfo],
],
allow_files = False,
),
"runtime_deps": attr.label_list(
doc = """Libraries to make available to the final binary or test at runtime only. Like ordinary deps, these will
appear on the runtime classpath, but unlike them, not on the compile-time classpath.""",
default = [],
allow_files = False,
),
"data": attr.label_list(
doc = """The list of files needed by this rule at runtime. See general comments about `data` at
[Attributes common to all build rules](https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes).""",
allow_files = True,
),
"associates": attr.label_list(
doc = """Kotlin deps who should be considered part of the same module/compilation-unit
for the purposes of "internal" access. Such deps must all share the same module space
and so a target cannot associate to two deps from two different modules.""",
default = [],
providers = [JavaInfo, _KtJvmInfo],
),
"plugins": attr.label_list(
default = [],
cfg = "exec",
providers = [
[_KtPluginConfiguration],
[_KtCompilerPluginInfo],
],
),
"module_name": attr.string(
doc = """The name of the module, if not provided the module name is derived from the label. --e.g.,
`//some/package/path:label_name` is translated to
`some_package_path-label_name`.""",
),
"kotlinc_opts": attr.label(
doc = """Kotlinc options to be used when compiling this target.""",
default = "//:default-kotlinc-opts",
providers = [KotlincOptions],
),
"javac_opts": attr.label(
doc = """Javac options to be used when compiling this target. These opts if provided will
be used instead of the ones provided to the toolchain.
Use --@rules_jvm:default-kotlinc-opts=//:my-custom-settings to point to a custom global default options in .bazelrc
""",
default = None,
providers = [_JavacOptions],
),
"_jdeps_merger": attr.label(
doc = "the jdeps merger executable",
default = "//src/misc:worker-native",
# default = "//src/misc:worker-jvm",
executable = True,
allow_files = True,
cfg = "exec",
),
"_kotlin_builder": attr.label(
default = "//src/kotlin-builder:worker-jvm",
executable = True,
allow_files = True,
cfg = "exec",
),
},
)
# we cannot use attr.output because it may break compatibility (requesting default output as `.jar` or `-sources.jar`), including IntelliJ IDEA plugin
common_outputs = dict(
jar = "%{name}.jar",
srcjar = "%{name}-sources.jar",
)

View File

@@ -0,0 +1,21 @@
visibility("private")
def init_builder_args(ctx, rule_kind, module_name, toolchain):
"""Initialize an arg object for a task that will be executed by the Kotlin Builder."""
args = ctx.actions.args()
args.set_param_file_format("multiline")
args.use_param_file("--flagfile=%s", use_always = True)
args.add("--target_label", ctx.label)
args.add("--rule_kind", rule_kind)
args.add("--kotlin_module_name", module_name)
debug = toolchain.debug
for tag in ctx.attr.tags:
if tag == "trace":
debug = debug + [tag]
if tag == "timings":
debug = debug + [tag]
args.add_all("--kotlin_debug_tags", debug, omit_if_empty = True)
return args

View File

@@ -0,0 +1,797 @@
# Copyright 2018 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",
"JavaInfo",
"java_common",
)
load(
"@rules_kotlin//kotlin/internal:defs.bzl",
_JAVA_RUNTIME_TOOLCHAIN_TYPE = "JAVA_RUNTIME_TOOLCHAIN_TYPE",
_JAVA_TOOLCHAIN_TYPE = "JAVA_TOOLCHAIN_TYPE",
_KtCompilerPluginInfo = "KtCompilerPluginInfo",
_KtJvmInfo = "KtJvmInfo",
_KtPluginConfiguration = "KtPluginConfiguration",
_TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE",
)
load(
"@rules_kotlin//kotlin/internal:opts.bzl",
"JavacOptions",
"javac_options_to_flags",
)
load(
"@rules_kotlin//kotlin/internal/jvm:associates.bzl",
_associate_utils = "associate_utils",
)
load(
"@rules_kotlin//kotlin/internal/jvm:plugins.bzl",
"is_ksp_processor_generating_java",
_plugin_mappers = "mappers",
)
load(
"@rules_kotlin//kotlin/internal/utils:sets.bzl",
_sets = "sets",
)
load(
"@rules_kotlin//kotlin/internal/utils:utils.bzl",
_utils = "utils",
)
load(
"//:rules/impl/builder-args.bzl",
"init_builder_args",
)
load(
"//:rules/impl/kotlinc-options.bzl",
"kotlinc_options_to_flags",
"KotlincOptions"
)
# UTILITY ##############################################################################################################
def find_java_toolchain(ctx, target):
if _JAVA_TOOLCHAIN_TYPE in ctx.toolchains:
return ctx.toolchains[_JAVA_TOOLCHAIN_TYPE].java
return target[java_common.JavaToolchainInfo]
def find_java_runtime_toolchain(ctx, target):
if _JAVA_RUNTIME_TOOLCHAIN_TYPE in ctx.toolchains:
return ctx.toolchains[_JAVA_RUNTIME_TOOLCHAIN_TYPE].java_runtime
return target[java_common.JavaRuntimeInfo]
def _java_info(target):
return target[JavaInfo] if JavaInfo in target else None
def _deps_artifacts(toolchain, targets):
"""Collect Jdeps artifacts if required."""
deps_artifacts = [t[JavaInfo].outputs.jdeps for t in targets if JavaInfo in t and t[JavaInfo].outputs.jdeps] if toolchain.experimental_report_unused_deps else []
return depset(deps_artifacts)
def _partitioned_srcs(srcs):
"""Creates a struct of srcs sorted by extension. Fails if there are no sources."""
kt_srcs = []
java_srcs = []
src_jars = []
for f in srcs:
if f.path.endswith(".kt"):
kt_srcs.append(f)
elif f.path.endswith(".java"):
java_srcs.append(f)
elif f.path.endswith(".srcjar"):
src_jars.append(f)
return struct(
kt = kt_srcs,
java = java_srcs,
all_srcs = kt_srcs + java_srcs,
src_jars = src_jars,
)
def _compiler_toolchains(ctx):
"""Creates a struct of the relevant compilation toolchains"""
return struct(
kt = ctx.toolchains[_TOOLCHAIN_TYPE],
java = find_java_toolchain(ctx, ctx.attr._java_toolchain),
java_runtime = find_java_runtime_toolchain(ctx, ctx.attr._host_javabase),
)
def _compute_transitive_jars(dep_infos, prune_transitive_deps):
compile_jars = [d.compile_jars for d in dep_infos]
if prune_transitive_deps:
return compile_jars
transitive_compile_time_jars = [d.transitive_compile_time_jars for d in dep_infos]
return compile_jars + transitive_compile_time_jars
def _jvm_deps(ctx, toolchains, associated_targets, deps, runtime_deps = []):
"""Encapsulates jvm dependency metadata."""
diff = _sets.intersection(
_sets.copy_of([x.label for x in associated_targets]),
_sets.copy_of([x.label for x in deps]),
)
if diff:
fail(
"\n------\nTargets should only be put in associates= or deps=, not both:\n%s" %
",\n ".join([" %s" % x for x in list(diff)]),
)
dep_infos = [_java_info(d) for d in associated_targets + deps] + [toolchains.kt.jvm_stdlibs]
# reduced classpath, exclude transitive deps from compilation
prune_transitive_deps = toolchains.kt.experimental_prune_transitive_deps and "kt_experimental_prune_transitive_deps_incompatible" not in ctx.attr.tags
transitive = _compute_transitive_jars(dep_infos, prune_transitive_deps)
return struct(
deps = dep_infos,
compile_jars = depset(
transitive = transitive,
),
runtime_deps = [_java_info(d) for d in runtime_deps],
)
def _java_infos_to_compile_jars(java_infos):
return depset(transitive = [j.compile_jars for j in java_infos])
def _exported_plugins(deps):
"""Encapsulates compiler dependency metadata."""
plugins = []
for dep in deps:
if _KtJvmInfo in dep and dep[_KtJvmInfo] != None:
plugins.extend(dep[_KtJvmInfo].exported_compiler_plugins.to_list())
return plugins
def _collect_plugins_for_export(local, exports):
"""Collects into a depset. """
return depset(
local,
transitive = [
e[_KtJvmInfo].exported_compiler_plugins
for e in exports
if _KtJvmInfo in e and e[_KtJvmInfo]
],
)
def _format_compile_plugin_options(o):
"""Format compiler option into id:value for cmd line."""
return [
"%s:%s" % (o.id, o.value),
]
def _new_plugins_from(targets):
"""Returns a struct containing the plugin metadata for the given targets.
Args:
targets: A list of targets.
Returns:
A struct containing the plugins for the given targets in the format:
{
stubs_phase = {
classpath = depset,
options= List[KtCompilerPluginOption],
),
compile = {
classpath = depset,
options = List[KtCompilerPluginOption],
},
}
"""
all_plugins = {}
plugins_without_phase = []
for t in targets:
if _KtCompilerPluginInfo not in t:
continue
plugin = t[_KtCompilerPluginInfo]
if not (plugin.stubs or plugin.compile):
plugins_without_phase.append("%s: %s" % (t.label, plugin.id))
if plugin.id in all_plugins:
# This need a more robust error messaging.
fail("has multiple plugins with the same id: %s." % plugin.id)
all_plugins[plugin.id] = plugin
if plugins_without_phase:
fail("has plugin without a phase defined: %s" % cfgs_without_plugin)
all_plugin_cfgs = {}
cfgs_without_plugin = []
for t in targets:
if _KtPluginConfiguration not in t:
continue
cfg = t[_KtPluginConfiguration]
if cfg.id not in all_plugins:
cfgs_without_plugin.append("%s: %s" % (t.label, cfg.id))
all_plugin_cfgs[cfg.id] = cfg
if cfgs_without_plugin:
fail("has plugin configurations without corresponding plugins: %s" % cfgs_without_plugin)
return struct(
stubs_phase = _new_plugin_from(all_plugin_cfgs, [p for p in all_plugins.values() if p.stubs]),
compile_phase = _new_plugin_from(all_plugin_cfgs, [p for p in all_plugins.values() if p.compile]),
)
def _new_plugin_from(all_cfgs, plugins_for_phase):
classpath = []
data = []
options = []
for p in plugins_for_phase:
classpath.append(p.classpath)
options.extend(p.options)
if p.id in all_cfgs:
cfg = all_cfgs[p.id]
classpath.append(cfg.classpath)
data.append(cfg.data)
options.extend(cfg.options)
return struct(
classpath = depset(transitive = classpath),
data = depset(transitive = data),
options = options,
)
# INTERNAL ACTIONS #####################################################################################################
def _fold_jars_action(ctx, rule_kind, toolchains, output_jar, input_jars, action_type = ""):
"""Set up an action to Fold the input jars into a normalized output jar."""
args = ctx.actions.args()
args.add_all([
"--normalize",
"--exclude_build_data",
"--add_missing_directories",
])
args.add_all([
"--deploy_manifest_lines",
"Target-Label: %s" % str(ctx.label),
"Injecting-Rule-Kind: %s" % rule_kind,
])
args.add("--output", output_jar)
args.add_all(input_jars, before_each = "--sources")
ctx.actions.run(
mnemonic = "KotlinFoldJars" + action_type,
inputs = input_jars,
outputs = [output_jar],
executable = toolchains.java.single_jar,
arguments = [args],
progress_message = "Merging Kotlin output jar %%{label}%s from %d inputs" % (
"" if not action_type else " (%s)" % action_type,
len(input_jars),
),
)
def _run_merge_jdeps_action(ctx, toolchain, jdeps, output, deps):
"""Creates a Jdeps merger action invocation."""
mnemonic = "JdepsMerge"
progress_message = "%s %%{label} { jdeps: %d }" % (
mnemonic,
len(jdeps),
)
inputs = depset(jdeps)
if not toolchain.experimental_report_unused_deps == "off":
# for sandboxing to work, and for this action to be deterministic, the compile jars need to be passed as inputs
inputs = depset(jdeps, transitive = [depset([], transitive = [dep.transitive_compile_time_jars for dep in deps])])
ctx.actions.run(
mnemonic = mnemonic,
inputs = inputs,
outputs = [output],
use_default_shell_env = True,
executable = ctx.attr._jdeps_merger.files_to_run.executable,
execution_requirements = {
"supports-workers": "1",
"supports-multiplex-workers": "1",
"supports-worker-cancellation": "1",
},
arguments = ["--flagfile=|jdeps|" + output.path + "|" + str(ctx.label) + "|" + toolchain.experimental_report_unused_deps],
progress_message = progress_message,
)
def _run_ksp_builder_actions(
ctx,
rule_kind,
toolchains,
srcs,
associates,
compile_deps,
deps_artifacts,
annotation_processors,
transitive_runtime_jars,
plugins):
"""Runs KSP using the KotlinBuilder tool
Returns:
A struct containing KSP outputs
"""
ksp_generated_java_srcjar = ctx.actions.declare_file(ctx.label.name + "-ksp-kt-gensrc.jar")
_run_kt_builder_action(
ctx = ctx,
rule_kind = rule_kind,
toolchains = toolchains,
srcs = srcs,
generated_src_jars = [],
associates = associates,
compile_deps = compile_deps,
deps_artifacts = deps_artifacts,
annotation_processors = annotation_processors,
transitive_runtime_jars = transitive_runtime_jars,
plugins = plugins,
outputs = {
"ksp_generated_java_srcjar": ksp_generated_java_srcjar,
},
build_kotlin = False,
mnemonic = "KotlinKsp",
)
return ksp_generated_java_srcjar
def _run_kt_builder_action(
ctx,
rule_kind,
toolchains,
srcs,
generated_src_jars,
associates,
compile_deps,
deps_artifacts,
annotation_processors,
transitive_runtime_jars,
plugins,
outputs,
build_kotlin = True,
mnemonic = "KotlinCompile"):
"""Creates a KotlinBuilder action invocation."""
kotlinc_options = ctx.attr.kotlinc_opts[KotlincOptions]
javac_options = ctx.attr.javac_opts[JavacOptions] if ctx.attr.javac_opts else toolchains.kt.javac_options
args = init_builder_args(ctx, rule_kind, associates.module_name, toolchains.kt)
for f, path in outputs.items():
args.add("--" + f, path)
kotlinc_options_to_flags(kotlinc_options, args)
args.add_all("--opt_in", kotlinc_options.opt_in)
# args.add_all("--javacopts", javac_options_to_flags(javac_options))
args.add_all("--direct_dependencies", _java_infos_to_compile_jars(compile_deps.deps))
args.add_all("--classpath", compile_deps.compile_jars)
# if not toolchains.kt.experimental_reduce_classpath_mode == "NONE":
# args.add("--reduced_classpath_mode", "true")
args.add_all("--deps_artifacts", deps_artifacts)
args.add_all("--kotlin_friend_paths", associates.jars, map_each = _associate_utils.flatten_jars)
if ctx.coverage_instrumented():
args.add("--instrument_coverage")
# collect and prepare plugin descriptor for the worker
args.add_all(
"--processors",
annotation_processors,
map_each = _plugin_mappers.kt_plugin_to_processor,
omit_if_empty = True,
uniquify = True,
)
args.add_all(
"--processor_path",
annotation_processors,
map_each = _plugin_mappers.kt_plugin_to_processorpath,
omit_if_empty = True,
uniquify = True,
)
args.add_all(
"--stubs_plugin_classpath",
plugins.stubs_phase.classpath,
omit_if_empty = True,
)
args.add_all(
"--stubs_plugin_options",
plugins.stubs_phase.options,
map_each = _format_compile_plugin_options,
omit_if_empty = True,
)
args.add_all(
"--compiler_plugin_classpath",
plugins.compile_phase.classpath,
omit_if_empty = True,
)
args.add_all(
"--compiler_plugin_options",
plugins.compile_phase.options,
map_each = _format_compile_plugin_options,
omit_if_empty = True,
)
if not build_kotlin:
args.add("--build_kotlin", False)
progress_message = "%s %%{label} { kt: %d, java: %d, srcjars: %d } for %s" % (
mnemonic,
len(srcs.kt),
len(srcs.java),
len(srcs.src_jars),
ctx.var.get("TARGET_CPU", "UNKNOWN CPU"),
)
ctx.actions.run(
mnemonic = mnemonic,
inputs = depset(
srcs.all_srcs + srcs.src_jars + generated_src_jars,
transitive = [
compile_deps.compile_jars,
transitive_runtime_jars,
deps_artifacts,
plugins.stubs_phase.classpath,
plugins.compile_phase.classpath,
],
),
use_default_shell_env = True,
outputs = [f for f in outputs.values()],
executable = ctx.attr._kotlin_builder.files_to_run.executable,
execution_requirements = {
"worker-key-mnemonic": mnemonic,
"supports-workers": "1",
"supports-multiplex-workers": "1",
"supports-worker-cancellation": "1",
},
arguments = [args],
progress_message = progress_message,
env = {
"LC_CTYPE": "en_US.UTF-8",
"REPOSITORY_NAME": _utils.builder_workspace_name(ctx),
},
)
# MAIN ACTIONS #########################################################################################################
def kt_jvm_produce_jar_actions(ctx, rule_kind):
"""This macro sets up a compile action for a Kotlin jar.
Args:
ctx: Invoking rule ctx, used for attr, actions, and label.
rule_kind: The rule kind --e.g., `kt_jvm_library`.
Returns:
A struct containing the providers JavaInfo (`java`) and `kt` (KtJvmInfo). This struct is not intended to be
used as a legacy provider -- rather the caller should transform the result.
"""
toolchains = _compiler_toolchains(ctx)
srcs = _partitioned_srcs(ctx.files.srcs)
associates = _associate_utils.get_associates(ctx)
compile_deps = _jvm_deps(
ctx,
toolchains,
associates.targets,
deps = ctx.attr.deps,
runtime_deps = ctx.attr.runtime_deps,
)
perTargetPlugins = ctx.attr.plugins if hasattr(ctx.attr, "plugins") else []
annotation_processors = _plugin_mappers.targets_to_annotation_processors(perTargetPlugins + ctx.attr.deps)
ksp_annotation_processors = _plugin_mappers.targets_to_ksp_annotation_processors(perTargetPlugins + ctx.attr.deps)
transitive_runtime_jars = _plugin_mappers.targets_to_transitive_runtime_jars(perTargetPlugins + ctx.attr.deps)
plugins = _new_plugins_from(perTargetPlugins + _exported_plugins(deps = ctx.attr.deps))
toolchain = toolchains.kt
deps_artifacts = _deps_artifacts(toolchain, ctx.attr.deps + associates.targets)
# merge outputs into final runtime jar
output_jar = ctx.actions.declare_file(ctx.label.name + ".jar")
outputs_struct = _run_kt_java_builder_actions(
ctx = ctx,
output_jar = output_jar,
rule_kind = rule_kind,
toolchains = toolchains,
srcs = srcs,
generated_ksp_src_jars = [],
associates = associates,
compile_deps = compile_deps,
deps_artifacts = deps_artifacts,
annotation_processors = annotation_processors,
ksp_annotation_processors = ksp_annotation_processors,
transitive_runtime_jars = transitive_runtime_jars,
plugins = plugins,
)
compile_jar = outputs_struct.compile_jar
generated_src_jars = outputs_struct.generated_src_jars
annotation_processing = outputs_struct.annotation_processing
source_jar = java_common.pack_sources(
ctx.actions,
output_source_jar = ctx.outputs.srcjar,
sources = srcs.kt + srcs.java,
source_jars = srcs.src_jars + generated_src_jars,
java_toolchain = toolchains.java,
)
generated_source_jar = java_common.pack_sources(
ctx.actions,
output_source_jar = ctx.actions.declare_file(ctx.label.name + "-gensrc.jar"),
source_jars = generated_src_jars,
java_toolchain = toolchains.java,
) if generated_src_jars else None
java_info = JavaInfo(
output_jar = output_jar,
compile_jar = compile_jar,
source_jar = source_jar,
jdeps = outputs_struct.output_jdeps,
deps = compile_deps.deps,
runtime_deps = [_java_info(d) for d in ctx.attr.runtime_deps],
exports = [_java_info(d) for d in getattr(ctx.attr, "exports", [])],
neverlink = getattr(ctx.attr, "neverlink", False),
generated_source_jar = generated_source_jar,
)
instrumented_files = coverage_common.instrumented_files_info(
ctx,
source_attributes = ["srcs"],
dependency_attributes = ["deps", "exports", "associates"],
extensions = ["kt", "java"],
)
return struct(
java = java_info,
instrumented_files = instrumented_files,
kt = _KtJvmInfo(
srcs = ctx.files.srcs,
module_name = associates.module_name,
module_jars = associates.jars,
language_version = toolchain.api_version,
exported_compiler_plugins = _collect_plugins_for_export(
getattr(ctx.attr, "exported_compiler_plugins", []),
getattr(ctx.attr, "exports", []),
),
# intellij aspect needs this
outputs = struct(
jdeps = outputs_struct.output_jdeps,
jars = [struct(
class_jar = output_jar,
ijar = compile_jar,
source_jars = [source_jar],
)],
),
transitive_compile_time_jars = java_info.transitive_compile_time_jars,
transitive_source_jars = java_info.transitive_source_jars,
annotation_processing = annotation_processing,
additional_generated_source_jars = generated_src_jars,
all_output_jars = [output_jar],
),
)
def _get_or_create_single_jdeps_output(toolchain, java_infos, ctx, compile_deps):
jdeps = [java_info.outputs.jdeps for java_info in java_infos if java_info.outputs.jdeps]
if len(jdeps) == 1:
return jdeps[0]
elif jdeps:
output_jdeps = ctx.actions.declare_file(ctx.label.name + ".jdeps")
_run_merge_jdeps_action(
ctx = ctx,
toolchain = toolchain,
jdeps = jdeps,
deps = compile_deps.deps,
output = output_jdeps,
)
def _compile_java_sources(ctx, output, srcs, generated_ksp_src_jars, compile_deps, kt_stubs_for_java, toolchains, strict_deps):
"""Compiles Java sources if present, otherwise uses KT ABI jar."""
javac_options = javac_options_to_flags(
ctx.attr.javac_opts[JavacOptions] if ctx.attr.javac_opts else toolchains.kt.javac_options,
)
if srcs.kt:
# assemblage consideration for Kotlin annotation processing
javac_options.append("-proc:none")
return java_common.compile(
ctx,
source_files = srcs.java,
source_jars = srcs.src_jars + generated_ksp_src_jars,
output = output,
deps = compile_deps.deps + ([] if kt_stubs_for_java == None else [kt_stubs_for_java]),
java_toolchain = toolchains.java,
plugins = _plugin_mappers.targets_to_annotation_processors_java_plugin_info(ctx.attr.plugins) if hasattr(ctx.attr, "plugins") else [],
javac_opts = javac_options,
neverlink = getattr(ctx.attr, "neverlink", False),
strict_deps = strict_deps,
)
def _run_kt_java_builder_actions(
ctx,
output_jar,
rule_kind,
toolchains,
srcs,
generated_ksp_src_jars,
associates,
compile_deps,
deps_artifacts,
annotation_processors,
ksp_annotation_processors,
transitive_runtime_jars,
plugins):
"""Runs the necessary KotlinBuilder and JavaBuilder actions to compile a jar
Returns:
A struct containing the a list of output_jars and a struct annotation_processing jars
"""
has_kt_sources = srcs.kt or srcs.src_jars
# run KSP
if has_kt_sources and ksp_annotation_processors:
ksp_generated_class_jar = _run_ksp_builder_actions(
ctx,
rule_kind = rule_kind,
toolchains = toolchains,
srcs = srcs,
associates = associates,
compile_deps = compile_deps,
deps_artifacts = deps_artifacts,
annotation_processors = ksp_annotation_processors,
transitive_runtime_jars = transitive_runtime_jars,
plugins = plugins,
)
generated_ksp_src_jars.append(ksp_generated_class_jar)
java_infos = []
outputs = None
kt_compile_jar = None
toolchain = toolchains.kt
kt_stubs_for_java = None
kt_output_jar = None
has_java_sources = srcs.java or srcs.src_jars or (generated_ksp_src_jars and is_ksp_processor_generating_java(ctx.attr.plugins))
jvm_emit_jdeps = toolchain.jvm_emit_jdeps
# jvm_emit_jdeps = False
# build Kotlin
if has_kt_sources:
kt_output_jar = ctx.actions.declare_file(ctx.label.name + "-kt.jar") if has_java_sources else output_jar
if not "kt_abi_plugin_incompatible" in ctx.attr.tags:
kt_compile_jar = ctx.actions.declare_file(ctx.label.name + ("-kt.abi.jar" if has_java_sources else ".abi.jar"))
outputs = {
"output": kt_output_jar,
"abi_jar": kt_compile_jar,
}
else:
kt_compile_jar = kt_output_jar
outputs = {
"output": kt_output_jar,
}
kt_jdeps = None
if jvm_emit_jdeps:
kt_jdeps = ctx.actions.declare_file(ctx.label.name + "-kt.jdeps")
outputs["kotlin_output_jdeps"] = kt_jdeps
_run_kt_builder_action(
ctx = ctx,
rule_kind = rule_kind,
toolchains = toolchains,
srcs = srcs,
generated_src_jars = generated_ksp_src_jars,
associates = associates,
compile_deps = compile_deps,
deps_artifacts = deps_artifacts,
annotation_processors = [],
transitive_runtime_jars = transitive_runtime_jars,
plugins = plugins,
outputs = outputs,
build_kotlin = True,
mnemonic = "KotlinCompile",
)
if not annotation_processors or not srcs.kt:
kt_stubs_for_java = JavaInfo(compile_jar = kt_compile_jar, output_jar = kt_output_jar, neverlink = True)
kt_java_info = JavaInfo(
output_jar = kt_output_jar,
compile_jar = kt_compile_jar,
jdeps = kt_jdeps,
deps = compile_deps.deps,
runtime_deps = [d[JavaInfo] for d in ctx.attr.runtime_deps],
exports = [d[JavaInfo] for d in getattr(ctx.attr, "exports", [])],
neverlink = getattr(ctx.attr, "neverlink", False),
)
java_infos.append(kt_java_info)
compile_jar = kt_compile_jar
ap_generated_src_jar = None
if has_java_sources:
java_output_jar = output_jar if kt_output_jar == None else ctx.actions.declare_file(ctx.label.name + "-java.jar")
java_part_java_info = _compile_java_sources(
ctx = ctx,
output = java_output_jar,
srcs = srcs,
generated_ksp_src_jars = generated_ksp_src_jars,
compile_deps = compile_deps,
kt_stubs_for_java = kt_stubs_for_java,
toolchains = toolchains,
strict_deps = toolchain.experimental_strict_kotlin_deps,
)
java_infos.append(java_part_java_info)
ap_generated_src_jar = java_part_java_info.annotation_processing.source_jar
compile_jars = [jars.ijar for jars in java_part_java_info.java_outputs]
output_jars = [jars.class_jar for jars in java_part_java_info.java_outputs]
if kt_output_jar == None:
if not len(output_jars) == 1:
fail("expect the only output")
if not len(compile_jars) == 1:
fail("expect the only compile_jar")
if not output_jars[0] == java_output_jar:
fail("java_output is not equal to result")
compile_jar = compile_jars[0]
else:
_fold_jars_action(
ctx,
rule_kind = rule_kind,
toolchains = toolchains,
output_jar = output_jar,
action_type = "Runtime",
input_jars = [kt_output_jar, java_output_jar],
)
# merge ABI jars into final compile jar
compile_jar = ctx.actions.declare_file(ctx.label.name + ".abi.jar")
_fold_jars_action(
ctx,
rule_kind = rule_kind,
toolchains = toolchains,
output_jar = compile_jar,
action_type = "Abi",
input_jars = compile_jars + [kt_compile_jar],
)
elif kt_output_jar == None:
ctx.actions.symlink(output = output_jar, target_file = toolchain.empty_jar)
annotation_processing = None
if annotation_processors:
outputs_list = [java_info.outputs for java_info in java_infos]
annotation_processing = _create_annotation_processing(
annotation_processors = annotation_processors,
ap_class_jar = [jars.class_jar for outputs in outputs_list for jars in outputs.jars][0],
ap_source_jar = ap_generated_src_jar,
)
return struct(
compile_jar = compile_jar,
generated_src_jars = generated_ksp_src_jars,
annotation_processing = annotation_processing,
output_jdeps = _get_or_create_single_jdeps_output(toolchain, java_infos, ctx, compile_deps) if jvm_emit_jdeps else None,
)
def _create_annotation_processing(annotation_processors, ap_class_jar, ap_source_jar):
"""Creates the annotation_processing field for Kt to match what JavaInfo
The Bazel Plugin IDE logic is based on this assumption in order to locate the Annotation
Processor generated source code.
See https://docs.bazel.build/versions/master/skylark/lib/JavaInfo.html#annotation_processing
"""
if annotation_processors:
return struct(
enabled = True,
class_jar = ap_class_jar,
source_jar = ap_source_jar,
)
return None

View File

@@ -0,0 +1,382 @@
_KOPTS = {
"warn": struct(
args = dict(
default = "",
doc = "Control warning behaviour.",
values = ["off", "error"],
),
type = attr.string,
flag_name = "warn",
),
# "include_stdlibs": struct(
# args = dict(
# default = "all",
# doc = "Don't automatically include the Kotlin standard libraries into the classpath (stdlib and reflect).",
# values = ["all", "stdlib", "none"],
# ),
# type = attr.string,
# value_to_flag = {
# "all": None,
# "stdlib": ["-no-reflect"],
# "none": ["-no-stdlib"],
# },
# ),
# "x_skip_prerelease_check": struct(
# flag = "-Xskip-prerelease-check",
# args = dict(
# default = False,
# doc = "Suppress errors thrown when using pre-release classes.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xskip-prerelease-check"],
# },
# ),
"context_receivers": struct(
args = dict(
default = False,
doc = "Enable experimental context receivers.",
),
type = attr.bool,
flag_name = "context-receivers",
),
# "x_suppress_version_warnings": struct(
# flag = "-Xsuppress-version-warnings",
# args = dict(
# default = False,
# doc = "Suppress warnings about outdated, inconsistent, or experimental language or API versions.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xsuppress-version-warnings"],
# },
# ),
"x_inline_classes": struct(
args = dict(
default = False,
doc = "Enable experimental inline classes",
),
type = attr.bool,
flag_name = "inline-classes",
),
# "x_allow_result_return_type": struct(
# flag = "-Xallow-result-return-type",
# args = dict(
# default = False,
# doc = "Enable kotlin.Result as a return type",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xallow-result-return-type"],
# },
# ),
"jvm_default": struct(
args = dict(
default = "",
doc = "Specifies that a JVM default method should be generated for non-abstract Kotlin interface member.",
values = ["all-compatibility", "all"],
),
type = attr.string,
flag_name = "jvm-default",
),
# "x_no_call_assertions": struct(
# flag = "-Xno-call-assertions",
# args = dict(
# default = False,
# doc = "Don't generate not-null assertions for arguments of platform types",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-call-assertions"],
# },
# ),
# "x_no_param_assertions": struct(
# flag = "-Xno-param-assertions",
# args = dict(
# default = False,
# doc = "Don't generate not-null assertions on parameters of methods accessible from Java",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-param-assertions"],
# },
# ),
# "x_no_receiver_assertions": struct(
# flag = "-Xno-receiver-assertions",
# args = dict(
# default = False,
# doc = "Don't generate not-null assertion for extension receiver arguments of platform types",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-receiver-assertions"],
# },
# ),
# "x_no_optimized_callable_references": struct(
# flag = "-Xno-optimized-callable-references",
# args = dict(
# default = False,
# doc = "Do not use optimized callable reference superclasses. Available from 1.4.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-optimized-callable-references"],
# },
# ),
# "x_explicit_api_mode": struct(
# flag = "-Xexplicit-api",
# args = dict(
# default = "off",
# doc = "Enable explicit API mode for Kotlin libraries.",
# values = ["off", "warning", "strict"],
# ),
# type = attr.string,
# value_to_flag = {
# "off": None,
# "warning": ["-Xexplicit-api=warning"],
# "strict": ["-Xexplicit-api=strict"],
# },
# ),
# "java_parameters": struct(
# args = dict(
# default = False,
# doc = "Generate metadata for Java 1.8+ reflection on method parameters.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-java-parameters"],
# },
# ),
# "x_multi_platform": struct(
# flag = "-Xmulti-platform",
# args = dict(
# default = False,
# doc = "Enable experimental language support for multi-platform projects",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xmulti-platform"],
# },
# ),
# "x_sam_conversions": struct(
# flag = "-Xsam-conversions",
# args = dict(
# default = "class",
# doc = "Change codegen behavior of SAM/functional interfaces",
# values = ["class", "indy"],
# ),
# type = attr.string,
# value_to_flag = {
# "class": ["-Xsam-conversions=class"],
# "indy": ["-Xsam-conversions=indy"],
# },
# ),
"x_lambdas": struct(
flag = "-Xlambdas",
args = dict(
default = "",
doc = "Change codegen behavior of lambdas",
values = ["class", "indy"],
),
type = attr.string,
flag_name = "lambdas",
),
# "x_emit_jvm_type_annotations": struct(
# flag = "-Xemit-jvm-type-annotations",
# args = dict(
# default = False,
# doc = "Basic support for type annotations in JVM bytecode.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xemit-jvm-type-annotations"],
# },
# ),
"opt_in": struct(
args = dict(
default = [],
doc = "Define APIs to opt-in to.",
),
type = attr.string_list,
),
# "x_use_fir": struct(
# # 1.6
# flag = "-Xuse-fir",
# args = dict(
# default = False,
# doc = "Compile using the experimental Kotlin Front-end IR. Available from 1.6.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xuse-fir"],
# },
# ),
# "x_use_k2": struct(
# # 1.7
# flag = "-Xuse-k2",
# args = dict(
# default = False,
# doc = "Compile using experimental K2. K2 is a new compiler pipeline, no compatibility guarantees are yet provided",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xuse-k2"],
# },
# ),
# "x_no_optimize": struct(
# flag = "-Xno-optimize",
# args = dict(
# default = False,
# doc = "Disable optimizations",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-optimize"],
# },
# ),
# "x_backend_threads": struct(
# # 1.6.20, 1.7
# flag = "-Xbackend-threads",
# args = dict(
# default = 1,
# doc = "When using the IR backend, run lowerings by file in N parallel threads. 0 means use a thread per processor core. Default value is 1.",
# ),
# type = attr.int,
# value_to_flag = None,
# map_value_to_flag = _map_backend_threads_to_flag,
# ),
# "x_enable_incremental_compilation": struct(
# args = dict(
# default = False,
# doc = "Enable incremental compilation",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xenable-incremental-compilation"],
# },
# ),
# "x_report_perf": struct(
# flag = "-Xreport-perf",
# args = dict(
# default = False,
# doc = "Report detailed performance statistics",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xreport-perf"],
# },
# ),
# "x_use_fir_lt": struct(
# args = dict(
# default = False,
# doc = "Compile using LightTree parser with Front-end IR. Warning: this feature is far from being production-ready",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xuse-fir-lt"],
# },
# ),
"allow_kotlin_package": struct(
args = dict(
default = False,
doc = "",
),
type = attr.bool,
flag_name = "allow-kotlin-package",
),
# "x_no_source_debug_extension": struct(
# args = dict(
# default = False,
# doc = "Do not generate @kotlin.jvm.internal.SourceDebugExtension annotation on a class with the copy of SMAP",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xno-source-debug-extension"],
# },
# ),
# "x_type_enhancement_improvements_strict_mode": struct(
# args = dict(
# default = False,
# doc = "Enables strict mode for type enhancement improvements, enforcing stricter type checking and enhancements.",
# ),
# type = attr.bool,
# value_to_flag = {
# True: ["-Xtype-enhancement-improvements-strict-mode"],
# },
# ),
# "x_jsr_305": struct(
# args = dict(
# default = "",
# doc = "Specifies how to handle JSR-305 annotations in Kotlin code. Options are 'default', 'ignore', 'warn', and 'strict'.",
# values = ["ignore", "warn", "strict"],
# ),
# type = attr.string,
# flag_name = "jsr305",
# ),
# "x_assertions": struct(
# args = dict(
# default = None,
# doc = "Configures how assertions are handled. The 'jvm' option enables assertions in JVM code.",
# values = ["jvm"],
# ),
# type = attr.string,
# flag_name = "assertions",
# ),
# "x_jspecify_annotations": struct(
# args = dict(
# default = None,
# doc = "Controls how JSpecify annotations are treated. Options are 'default', 'ignore', 'warn', and 'strict'.",
# values = ["ignore", "warn", "strict"],
# ),
# type = attr.string,
# add_arg = attr.string,
# flag_name = "jspecify-annotations",
# ),
"jvm_target": struct(
args = dict(
default = "",
doc = "The -jvm_target flag. This is only tested at 1.8.",
values = ["1.6", "1.8", "9", "10", "11", "12", "13", "15", "16", "17"],
),
type = attr.string,
flag_name = "jvm-target",
),
# "x_jdk_release": struct(
# args = dict(
# default = "",
# doc = "The -jvm_target flag. This is only tested at 1.8.",
# values = ["1.6", "1.8", "9", "10", "11", "12", "13", "15", "16", "17"],
# ),
# type = attr.string,
# flag_name = "jdk_release",
# ),
}
# todo: experimental_strict_kotlin_deps, experimental_reduce_classpath_mode
KotlincOptions = provider(
fields = {
name: o.args["doc"]
for name, o in _KOPTS.items()
},
)
def _kotlinc_options_impl(ctx):
return [KotlincOptions(**{n: getattr(ctx.attr, n, None) for n in _KOPTS})]
kt_kotlinc_options = rule(
implementation = _kotlinc_options_impl,
doc = "Define kotlin compiler options.",
provides = [KotlincOptions],
attrs = {n: o.type(**o.args) for n, o in _KOPTS.items()},
)
def kotlinc_options_to_flags(kotlinc_options, args):
for n, o in _KOPTS.items():
value = getattr(kotlinc_options, n, None)
if value:
flagName = getattr(o, "flag_name", None)
if flagName:
if value == True:
args.add("--" + flagName)
else:
args.add("--" + flagName, value)

View File

@@ -0,0 +1,33 @@
load("@rules_java//java:defs.bzl", "JavaInfo")
visibility("private")
def _jvm_import(ctx):
return JavaInfo(
output_jar = ctx.file.jar,
compile_jar = ctx.file.jar,
source_jar = ctx.file.source_jar,
runtime_deps = [dep[JavaInfo] for dep in ctx.attr.runtime_deps],
)
jvm_import = rule(
attrs = {
"jar": attr.label(
doc = """The jar listed here is equivalent to an export attribute.""",
allow_single_file = True,
),
"source_jar": attr.label(
doc = """The sources for the class jar.""",
allow_single_file = True,
),
"runtime_deps": attr.label_list(
doc = """Libraries to make available to the final binary or test at runtime only. Like ordinary deps, these will
appear on the runtime classpath, but unlike them, not on the compile-time classpath.""",
default = [],
allow_files = False,
providers = [JavaInfo],
),
},
provides = [JavaInfo],
implementation = _jvm_import,
)

View File

@@ -0,0 +1,89 @@
load("@rules_java//java:defs.bzl", _JavaInfo = "JavaInfo")
load("@rules_kotlin//kotlin/internal:defs.bzl", _KtJvmInfo = "KtJvmInfo")
load("//:rules/common-attrs.bzl", "add_dicts", "common_attr", "common_outputs", "common_toolchains")
load("//:rules/impl/compile.bzl", "kt_jvm_produce_jar_actions")
visibility("private")
_lib_common_attr = add_dicts(common_attr, {
"srcs": attr.label_list(
doc = """The list of source files that are processed to create the target, this can contain both Java and Kotlin
files. Java analysis occurs first so Kotlin classes may depend on Java classes in the same compilation unit.""",
default = [],
allow_files = [".kt", ".java"],
mandatory = True,
),
"exports": attr.label_list(
doc = """\
Exported libraries.
Deps listed here will be made available to other rules, as if the parents explicitly depended on
these deps. This is not true for regular (non-exported) deps.""",
default = [],
providers = [JavaInfo],
),
# "exported_compiler_plugins": attr.label_list(
# doc = """\
# Exported compiler plugins.
#
# Compiler plugins listed here will be treated as if they were added in the plugins attribute
# of any targets that directly depend on this target. Unlike `java_plugin`s exported_plugins,
# this is not transitive""",
# default = [],
# providers = [[_KtCompilerPluginInfo], [KtPluginConfiguration]],
# ),
"neverlink": attr.bool(
doc = """If true only use this library for compilation and not at runtime.""",
default = False,
),
"_empty_jar": attr.label(
doc = """Empty jar for exporting JavaInfos.""",
allow_single_file = True,
cfg = "target",
default = Label("@rules_kotlin//third_party:empty.jar"),
),
"_empty_jdeps": attr.label(
doc = """Empty jdeps for exporting JavaInfos.""",
allow_single_file = True,
cfg = "target",
default = Label("@rules_kotlin//third_party:empty.jdeps"),
),
})
def _make_providers(ctx, providers):
files = [ctx.outputs.jar]
if providers.java.outputs.jdeps:
files.append(providers.java.outputs.jdeps)
return [
providers.java,
providers.kt,
providers.instrumented_files,
DefaultInfo(
files = depset(files),
runfiles = ctx.runfiles(
# explicitly include data files, otherwise they appear to be missing
files = ctx.files.data,
transitive_files = depset(order = "default"),
# continue to use collect_default until proper transitive data collecting is implemented.
collect_default = True,
),
),
]
def _jvm_library(ctx):
if ctx.attr.neverlink and ctx.attr.runtime_deps:
fail("runtime_deps and neverlink is nonsensical.", attr = "runtime_deps")
return _make_providers(ctx, kt_jvm_produce_jar_actions(ctx, "kt_jvm_library"))
jvm_library = rule(
doc = """This rule compiles and links Kotlin and Java sources into a .jar file.""",
attrs = _lib_common_attr,
outputs = common_outputs,
toolchains = common_toolchains,
fragments = ["java"], # required fragments of the target configuration
host_fragments = ["java"], # required fragments of the host configuration
implementation = _jvm_library,
provides = [_JavaInfo, _KtJvmInfo],
)

View File

@@ -0,0 +1,26 @@
load("@rules_java//java:defs.bzl", "JavaInfo")
visibility("private")
# rules_java creates empty JAR for each java_library target,
# so, we use rules_kotlin approach - use shared empty jar as output_jar (required attribute for JavaInfo)
def _jvm_provided_library(ctx):
return JavaInfo(
output_jar = ctx.file._empty_jar,
compile_jar = None,
exports = [ctx.attr.lib[JavaInfo]],
neverlink = True,
)
jvm_provided_library = rule(
attrs = {
"lib": attr.label(mandatory = True, allow_files = False, providers = [JavaInfo]),
"_empty_jar": attr.label(
doc = """Empty jar for exporting JavaInfos.""",
allow_single_file = True,
default = Label("@rules_kotlin//third_party:empty.jar"),
),
},
provides = [JavaInfo],
implementation = _jvm_provided_library,
)

View File

@@ -0,0 +1,70 @@
load("@rules_java//java:defs.bzl", "JavaInfo", "java_common")
visibility("private")
def _jvm_resources_impl(ctx):
resultJar = ctx.actions.declare_file(ctx.label.name + ".jar")
strip_prefix_dir = ctx.file.strip_prefix
strip_prefix = ""
if strip_prefix_dir == None:
strip_prefix = "|" + ctx.label.package
else:
strip_prefix = strip_prefix_dir.path
ctx.actions.run(
mnemonic = "PackageResources",
inputs = ctx.files.files,
# avoid creating small files on disk trick Bazel using this workaround
arguments = ["--flagfile=|jar|" + resultJar.path + "|" + strip_prefix],
# arguments = [args],
outputs = [resultJar],
use_default_shell_env = True,
executable = ctx.executable._worker,
execution_requirements = {
"supports-workers": "1",
"supports-multiplex-workers": "1",
"supports-worker-cancellation": "1",
},
env = {
# for Java source files
"LC_CTYPE": "en_US.UTF-8",
},
)
return [
DefaultInfo(
files = depset([resultJar]),
),
JavaInfo(
output_jar = resultJar,
# resource jar - should not be added as a compile-time dependency
compile_jar = None,
),
]
jvm_resources = rule(
doc = """This rule packages resources into a .jar file.""",
implementation = _jvm_resources_impl,
attrs = {
"files": attr.label_list(
doc = """The list of resource files to create the target""",
allow_files = True,
mandatory = True,
providers = ["FileProvider"],
),
"strip_prefix": attr.label(
doc = """The path prefix to strip from Java resources""",
allow_single_file = True,
providers = ["FileProvider"],
),
# see https://bazel.build/extending/rules#private_attributes_and_implicit_dependencies about implicit dependencies
"_worker": attr.label(
default = Label("//src/misc:worker-native"),
#default = Label("//src/misc:worker-jvm"),
executable = True,
allow_files = True,
cfg = "exec",
),
},
provides = [JavaInfo],
)

View File

@@ -0,0 +1,178 @@
load("//:rules/common-attrs.bzl", "add_dicts", "common_attr", "common_outputs", "common_toolchains")
load("//:rules/impl/compile.bzl", "kt_jvm_produce_jar_actions")
visibility("private")
_runnable_implicit_deps = {
"_java_runtime": attr.label(
default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
),
}
_runnable_common_attr = add_dicts(common_attr, _runnable_implicit_deps, {
"jvm_flags": attr.string_list(
doc = """A list of flags to embed in the wrapper script generated for running this binary. Note: does not yet
support make variable substitution.""",
default = [],
),
})
_SPLIT_STRINGS = [
"src/test/java/",
"src/test/kotlin/",
"javatests/",
"kotlin/",
"java/",
"test/",
]
def _is_absolute(path):
return path.startswith("/") or (len(path) > 2 and path[1] == ":")
def _write_launcher_action(ctx, rjars, main_class, jvm_flags):
"""Macro that writes out a launcher script shell script.
Args:
rjars: All of the runtime jars required to launch this java target.
main_class: the main class to launch.
jvm_flags: The flags that should be passed to the jvm.
args: Args that should be passed to the Binary.
"""
jvm_flags = " ".join([ctx.expand_location(f, ctx.attr.data) for f in jvm_flags])
template = ctx.attr._java_stub_template.files.to_list()[0]
java_runtime = ctx.toolchains["@bazel_tools//tools/jdk:runtime_toolchain_type"].java_runtime
java_bin_path = java_runtime.java_executable_exec_path
# Following https://github.com/bazelbuild/bazel/blob/6d5b084025a26f2f6d5041f7a9e8d302c590bc80/src/main/starlark/builtins_bzl/bazel/java/bazel_java_binary.bzl#L66-L67
# Enable the security manager past deprecation.
# On bazel 6, this check isn't possible...
if getattr(java_runtime, "version", 0) >= 17:
jvm_flags = jvm_flags + " -Djava.security.manager=allow"
classpath = ctx.configuration.host_path_separator.join(
["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list()],
)
ctx.actions.expand_template(
template = template,
output = ctx.outputs.executable,
substitutions = {
"%classpath%": classpath,
"%runfiles_manifest_only%": "",
"%java_start_class%": main_class,
"%javabin%": "JAVABIN=" + java_bin_path,
"%jvm_flags%": jvm_flags,
"%set_jacoco_metadata%": "",
"%set_jacoco_main_class%": "",
"%set_jacoco_java_runfiles_root%": "",
"%set_java_coverage_new_implementation%": """export JAVA_COVERAGE_NEW_IMPLEMENTATION=NO""",
"%workspace_prefix%": ctx.workspace_name + "/",
"%test_runtime_classpath_file%": "export TEST_RUNTIME_CLASSPATH_FILE=${JAVA_RUNFILES}",
"%needs_runfiles%": "0" if _is_absolute(java_bin_path) else "1",
},
is_executable = True,
)
return []
def _jvm_test(ctx):
providers = kt_jvm_produce_jar_actions(ctx, "kt_jvm_test")
runtime_jars = depset(ctx.files._bazel_test_runner, transitive = [providers.java.transitive_runtime_jars])
# coverage_runfiles = []
# if ctx.configuration.coverage_enabled:
# jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
# coverage_runfiles = jacocorunner.files.to_list()
test_class = ctx.attr.test_class
# If no test_class, do a best-effort attempt to infer one.
if not bool(ctx.attr.test_class):
for file in ctx.files.srcs:
package_relative_path = file.path.replace(ctx.label.package + "/", "")
if package_relative_path.split(".")[0] == ctx.attr.name:
for splitter in _SPLIT_STRINGS:
elements = file.short_path.split(splitter, 1)
if len(elements) == 2:
test_class = elements[1].split(".")[0].replace("/", ".")
break
jvm_flags = []
if hasattr(ctx.fragments.java, "default_jvm_opts"):
jvm_flags = ctx.fragments.java.default_jvm_opts
jvm_flags.extend(ctx.attr.jvm_flags)
coverage_metadata = _write_launcher_action(
ctx,
runtime_jars,
main_class = ctx.attr.main_class,
jvm_flags = [
"-ea",
"-Dbazel.test_suite=%s" % test_class,
] + jvm_flags,
)
# adds common test variables, including TEST_WORKSPACE
files = [ctx.outputs.jar]
if providers.java.outputs.jdeps:
files.append(providers.java.outputs.jdeps)
return [
providers.java,
providers.kt,
providers.instrumented_files,
DefaultInfo(
files = depset(files),
runfiles = ctx.runfiles(
# explicitly include data files, otherwise they appear to be missing
files = ctx.files.data,
transitive_files = depset(
order = "default",
transitive = [runtime_jars, depset(coverage_metadata)],
direct = ctx.files._java_runtime,
),
# continue to use collect_default until proper transitive data collecting is implemented.
collect_default = True,
),
),
] + [testing.TestEnvironment(environment = ctx.attr.env)]
jvm_test = rule(
doc = """\
Setup a simple kotlin_test.
**Notes:**
* The kotlin test library is not added implicitly, it is available with the label
`@rules_kotlin//kotlin/compiler:kotlin-test`.
""",
attrs = add_dicts(_runnable_common_attr, {
"srcs": attr.label_list(
doc = """The list of source files that are processed to create the target, this can contain both Java and Kotlin
files. Java analysis occurs first so Kotlin classes may depend on Java classes in the same compilation unit.""",
default = [],
allow_files = [".kt", ".java"],
),
"_bazel_test_runner": attr.label(
default = Label("@bazel_tools//tools/jdk:TestRunner_deploy.jar"),
allow_files = True,
),
"test_class": attr.string(
doc = "The Java class to be loaded by the test runner.",
default = "",
),
"main_class": attr.string(default = "com.google.testing.junit.runner.BazelTestRunner"),
"env": attr.string_dict(
doc = "Specifies additional environment variables to set when the target is executed by bazel test.",
default = {},
),
"_lcov_merger": attr.label(
default = Label("@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main"),
),
}),
executable = True,
outputs = common_outputs,
test = True,
toolchains = common_toolchains + ["@bazel_tools//tools/jdk:runtime_toolchain_type"],
implementation = _jvm_test,
fragments = ["java"], # Required fragments of the target configuration
host_fragments = ["java"], # Required fragments of the host configuration
)

View File

@@ -0,0 +1,10 @@
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
kt_jvm_library(
name = "jar",
srcs = glob(["*.kt"]),
deps = [
"//zip:build-zip",
],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,32 @@
package org.jetbrains.bazel.jvm.kotlin
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarFile
data class JarOwner(
@JvmField val jar: Path,
@JvmField val label: String? = null,
@JvmField val aspect: String? = null,
) {
companion object {
// These attributes are used by JavaBuilder, Turbine, and ijar.
// They must all be kept in sync.
@JvmField
val TARGET_LABEL = Attributes.Name("Target-Label")
@JvmField
val INJECTING_RULE_KIND = Attributes.Name("Injecting-Rule-Kind")
fun readJarOwnerFromManifest(jarPath: Path): JarOwner {
JarFile(jarPath.toFile()).use { jarFile ->
val manifest = jarFile.manifest ?: return JarOwner(jarPath)
val attributes = manifest.mainAttributes
val label =
attributes[TARGET_LABEL] as String?
?: return JarOwner(jarPath)
val injectingRuleKind = attributes[INJECTING_RULE_KIND] as String?
return JarOwner(jar = jarPath, label = label, aspect = injectingRuleKind)
}
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.
*
*/
package org.jetbrains.bazel.jvm.kotlin
import java.util.*
class ArgMap<T : Enum<T>>(
private val map: EnumMap<T, MutableList<String>>,
) {
/**
* Get the mandatory single value from a key
*/
fun mandatorySingle(key: T): String {
return requireNotNull(optionalSingle(key)) { "$key is not optional" }
}
fun optionalSingle(key: T): String? {
return map[key]?.let {
when (it.size) {
0 -> throw IllegalArgumentException("$key did not have a value")
1 -> it[0]
else -> throw IllegalArgumentException("$key should have a single value: $it")
}
}
}
fun boolFlag(key: T): Boolean {
val value = map[key] ?: return false
return when (value.size) {
0 -> true
1 -> value[0].toBoolean()
else -> throw IllegalArgumentException("$key should have a single value: $value")
}
}
fun mandatory(key: T): List<String> {
return map[key] ?: throw IllegalArgumentException("$key is not optional")
}
fun has(key: T): Boolean = map[key]?.isNotEmpty() ?: false
fun optional(key: T): List<String>? = map[key]
}
fun <T : Enum<T>> createArgMap(
args: List<String>,
enumClass: Class<T>,
): ArgMap<T> {
val result = EnumMap<T, MutableList<String>>(enumClass)
val keyString = args.first().also { require(it.startsWith("--")) { "first arg must be a flag" } }
.substring(2)
.uppercase()
.replace('-', '_')
var currentKey = java.lang.Enum.valueOf(enumClass, keyString)
val currentValue = mutableListOf<String>()
fun mergeCurrent() {
result.computeIfAbsent(currentKey) { mutableListOf() }.addAll(currentValue)
currentValue.clear()
}
for (it in args.drop(1)) {
if (it.startsWith("--")) {
mergeCurrent()
currentKey = java.lang.Enum.valueOf(enumClass, it.substring(2).uppercase().replace('-', '_'))
} else {
currentValue.add(it)
}
}
mergeCurrent()
return ArgMap(result)
}

View File

@@ -0,0 +1,49 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_jvm//:jvm.bzl", "jvm_import")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
kt_jvm_library(
name = "worker-lib",
srcs = glob(["*.kt"]),
deps = [
"//:kotlin-compiler",
":kotlin-build-tools-impl",
"//src/worker-framework",
"//src/jar",
"//zip:build-zip",
# we parse `jdeps` to support reduced classpath building and checking for unused deps
# (both features are disabled by default for now)
"@bazel_tools//src/main/protobuf:deps_java_proto",
"@rules_java//java/runfiles",
],
runtime_deps = [
"//src/kotlin-plugins/jdeps",
"//src/kotlin-plugins/abi",
"//:kotlin-metadata-jvm",
],
visibility = ["//visibility:public"],
)
java_binary(
name = "worker-jvm",
runtime_deps = [":worker-lib"],
main_class = "org.jetbrains.bazel.jvm.kotlin.KotlinBuildWorker",
data = [
"//src/kotlin-plugins/jdeps:resources",
"//src/kotlin-plugins/abi:resources",
],
jvm_flags = [
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
"-Dzip.handler.uses.crc.instead.of.timestamp=true",
"-Dkotlin.bazel.jdeps.plugin=$(rlocationpath //src/kotlin-plugins/jdeps:resources)",
"-Dkotlin.bazel.abi.plugin=$(rlocationpath //src/kotlin-plugins/abi:resources)",
],
visibility = ["//visibility:public"],
)
jvm_import(
name = "kotlin-build-tools-impl",
jar = "@kotlin-build-tools-impl_http//file",
source_jar = "@kotlin-build-tools-impl-sources_http//file",
)

View File

@@ -0,0 +1,102 @@
/*
* 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.
*
*/
package org.jetbrains.bazel.jvm.kotlin
/**
* CompilationArgs collects the arguments for executing the Kotlin compiler.
*/
class CompilationArgs(
val args: MutableList<String> = mutableListOf(),
) {
interface SetFlag {
fun flag(
name: String,
value: String,
): SetFlag
}
//fun plugin(p: CompilerPlugin): CompilationArgs = plugin(p) {}
fun plugin(
id: String,
flagArgs: SetFlag.() -> Unit,
) {
object : SetFlag {
override fun flag(
name: String,
value: String,
): SetFlag {
args.add("-P")
args.add("plugin:$id:$name=$value")
return this
}
}.flagArgs()
}
operator fun plus(other: CompilationArgs): CompilationArgs {
return CompilationArgs((args.asSequence() + other.args.asSequence()).toMutableList())
}
fun value(value: String): CompilationArgs {
args.add(value)
return this
}
fun append(compilationArgs: CompilationArgs): CompilationArgs {
args.addAll(compilationArgs.args)
return this
}
fun flag(value: String): CompilationArgs {
args.add(value)
return this
}
fun flag(
key: String,
value: () -> String,
): CompilationArgs {
args.add(key)
args.add(value())
return this
}
fun flag(
flag: String,
value: String,
): CompilationArgs {
args.add(flag)
args.add(value)
return this
}
fun values(values: Collection<String>): CompilationArgs {
args.addAll(values)
return this
}
fun xFlag(
flag: String,
value: String,
): CompilationArgs {
args.add("-X$flag=$value")
return this
}
fun toList(): List<String> = args.toList()
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2018 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.
*
*/
package org.jetbrains.bazel.jvm.kotlin
import java.io.Writer
internal class CompilationTaskContext(
private val label: String,
debug: List<String>,
@JvmField val out: Writer,
) {
private val start = System.currentTimeMillis()
private var timings: MutableList<String>?
private var level = -1
@JvmField
val isTracing: Boolean
init {
timings = if (debug.contains("timings")) mutableListOf() else null
isTracing = debug.contains("trace")
}
/**
* Print a list of debugging lines.
*
* @param header a header string
* @param lines a list of lines to print out
* @param prefix a prefix to add to each line
* @param filterEmpty if empty lines should be discarded or not
*/
fun printLines(
header: String,
lines: Sequence<String>,
prefix: String = "| ",
filterEmpty: Boolean = false,
) {
check(header.isNotEmpty())
out.appendLine(if (header.endsWith(":")) header else "$header:")
for (line in lines) {
if (line.isNotEmpty() || !filterEmpty) {
out.appendLine("$prefix$line")
}
}
out.appendLine()
}
inline fun whenTracing(block: CompilationTaskContext.() -> Unit) {
if (isTracing) block() else null
}
/**
* Runs a task and records the timings.
*/
inline fun <T> execute(
name: String,
task: () -> T,
): T = if (timings == null) task() else pushTimedTask(name, task)
private inline fun <T> pushTimedTask(
name: String,
task: () -> T,
): T {
level += 1
val previousTimings = timings
timings = mutableListOf()
try {
val start = System.currentTimeMillis()
val result = task()
val stop = System.currentTimeMillis()
previousTimings!!.add("${" ".repeat(level)} * $name: ${stop - start} ms")
previousTimings.addAll(timings!!)
return result
}
finally {
level -= 1
timings = previousTimings
}
}
/**
* This method should be called at the end of builder invocation.
*
* @param successful true if the task finished successfully.
*/
fun finalize(successful: Boolean) {
if (successful) {
timings?.also {
printLines(
"Task timings for $label (total: ${System.currentTimeMillis() - start} ms)",
it.asSequence(),
)
}
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2018 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.
*/
package org.jetbrains.bazel.jvm.kotlin
import org.jetbrains.intellij.build.io.AddDirEntriesMode
import org.jetbrains.intellij.build.io.PackageIndexBuilder
import org.jetbrains.intellij.build.io.ZipArchiveOutputStream
import org.jetbrains.intellij.build.io.file
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
import java.util.*
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.zip.ZipEntry
private val MANIFEST_NAME_BYTES = JarFile.MANIFEST_NAME.toByteArray()
/**
* A class for creating Jar files. Allows normalization of Jar entries by setting their timestamp to
* the DOS epoch. All Jar entries are sorted alphabetically.
*/
@Suppress("unused")
class JarCreator(
private val targetLabel: String,
private val injectingRuleKind: String,
private val out: ZipArchiveOutputStream,
private val packageIndexBuilder: PackageIndexBuilder,
) : AutoCloseable {
private var mainClass: String? = null
private var manifestFile: Path? = null
/**
* Adds the contents of a directory to the Jar file. All files below this directory will be added
* to the Jar file using the name relative to the directory as the name for the Jar entry.
*
* @param startDir the directory to add to the jar
*/
fun addDirectory(startDir: Path) {
val localPrefixLength = startDir.toString().length + 1
val dirCandidates = ArrayDeque<Path>()
dirCandidates.add(startDir)
val tempList = ArrayList<Path>()
while (true) {
val dir = dirCandidates.pollFirst() ?: break
tempList.clear()
val dirStream = try {
Files.newDirectoryStream(dir)
}
catch (_: NoSuchFileException) {
continue
}
dirStream.use {
tempList.addAll(it)
}
tempList.sort()
for (file in tempList) {
val attributes = Files.readAttributes(file, BasicFileAttributes::class.java)
var key = file.toString().substring(localPrefixLength).replace(File.separatorChar, '/')
if (attributes.isDirectory) {
dirCandidates.add(file)
}
else if (key == JarFile.MANIFEST_NAME) {
manifestFile = file
}
else {
packageIndexBuilder.addFile(key)
out.file(key, file)
}
}
}
}
private fun manifestContentImpl(existingFile: Path?): ByteArray {
val manifest = if (existingFile == null) {
Manifest()
}
else {
Files.newInputStream(existingFile).use { Manifest(it) }
}
var m = manifest
m.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
m.mainAttributes.putIfAbsent(Attributes.Name("Created-By"), "io.bazel.rules.kotlin")
val attributes = m.mainAttributes
if (mainClass != null) {
attributes[Attributes.Name.MAIN_CLASS] = mainClass
}
attributes[JarOwner.TARGET_LABEL] = targetLabel
attributes[JarOwner.INJECTING_RULE_KIND] = injectingRuleKind
val out = ByteArrayOutputStream()
manifest.write(out)
return out.toByteArray()
}
private fun writeManifest() {
packageIndexBuilder.addFile(JarFile.MANIFEST_NAME)
val content = manifestContentImpl(existingFile = manifestFile)
val size = content.size
out.writeDataRawEntry(
data = ByteBuffer.wrap(content),
name = MANIFEST_NAME_BYTES,
size = size,
compressedSize = size,
method = ZipEntry.STORED,
crc = 0,
)
}
override fun close() {
writeManifest()
packageIndexBuilder.writePackageIndex(stream = out, addDirEntriesMode = AddDirEntriesMode.RESOURCE_ONLY)
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.bazel.jvm.kotlin
import com.google.devtools.build.lib.worker.WorkerProtocol
import org.jetbrains.bazel.jvm.WorkRequestHandler
import java.io.Writer
import java.nio.file.Path
object KotlinBuildWorker {
@JvmStatic
fun main(startupArgs: Array<String>) {
WorkRequestHandler({ workRequest, consoleOutput, baseDir ->
handleRequest(workRequest, consoleOutput, baseDir)
})
.processRequests(startupArgs)
}
}
private fun handleRequest(
workRequest: WorkerProtocol.WorkRequest,
consoleOutput: Writer,
baseDir: Path,
): Int {
val sources = workRequest.inputsList.asSequence()
.filter { it.path.endsWith(".kt") || it.path.endsWith(".java") }
.map { baseDir.resolve(it.path).toFile() }
.toList()
return buildKotlin(workingDir = baseDir, args = workRequest.argumentsList, out = consoleOutput, sources = sources)
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2018 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.
*
*/
package org.jetbrains.bazel.jvm.kotlin
import java.io.File
import java.io.PrintWriter
import java.io.Writer
import java.nio.file.Files
import java.nio.file.Path
import java.util.regex.Pattern
import kotlin.io.path.ExperimentalPathApi
private val FLAG_FILE_RE = Pattern.compile("""^--flagfile=((.*)-(\d+).params)$""").toRegex()
internal fun buildKotlin(
workingDir: Path,
out: Writer,
args: List<String>,
sources: List<File>,
): Int {
check(args.isNotEmpty()) {
"expected at least a single arg got: ${args.joinToString(" ")}"
}
val argMap = createArgMap(
FLAG_FILE_RE.matchEntire(args[0])?.groups?.get(1)?.let {
Files.readAllLines(Path.of(it.value))
} ?: args,
enumClass = KotlinBuilderFlags::class.java,
)
val task = createBuildTask(argMap)
val compileContext = CompilationTaskContext(
label = task.label,
debug = argMap.optional(KotlinBuilderFlags.KOTLIN_DEBUG_TAGS) ?: emptyList(),
out = out
)
var success = false
try {
when (task.platform) {
Platform.JVM -> {
val task = createJvmTask(info = task, workingDir = workingDir, args = argMap)
if (compileContext.isTracing) {
compileContext.out.appendLine(formatDataClassToString(task.toString()))
}
compileContext.execute("compile classes") {
val code = compileContext.execute("kotlinc") {
doCompileKotlin(task = task, context = compileContext, sources = sources)
}
if (code != 0) {
return code
}
//packOutput(task, compileContext)
}
}
Platform.UNRECOGNIZED -> throw IllegalStateException("unrecognized platform: $task")
}
success = true
}
catch (e: CompilationStatusException) {
out.appendLine("Compilation failure: ${e.message}")
return e.status
}
catch (e: Throwable) {
out.appendLine(e.message ?: "unknown error")
PrintWriter(out).use { e.printStackTrace(it) }
}
finally {
compileContext.finalize(success)
}
return 0
}
private fun formatDataClassToString(input: String): CharSequence {
val indentUnit = " "
var currentIndent = ""
val result = StringBuilder()
for (char in input) {
when (char) {
'{', '[', '(' -> {
result.append("$char\n")
currentIndent += indentUnit
result.append(currentIndent)
}
'}', ']', ')' -> {
result.append("\n")
currentIndent = currentIndent.dropLast(indentUnit.length)
result.append(currentIndent).append(char)
}
',' -> result.append(",\n").append(currentIndent)
else -> result.append(char)
}
}
return result
}
private fun createBuildTask(argMap: ArgMap<KotlinBuilderFlags>): CompilationTaskInfo {
val ruleKind = argMap.mandatorySingle(KotlinBuilderFlags.RULE_KIND).split('_')
check(ruleKind.size == 3 && ruleKind[0] == "kt") {
"invalid rule kind $ruleKind"
}
return CompilationTaskInfo(
label = argMap.mandatorySingle(KotlinBuilderFlags.TARGET_LABEL),
ruleKind = checkNotNull(RuleKind.valueOf(ruleKind[2].uppercase())) {
"unrecognized rule kind ${ruleKind[2]}"
},
platform = checkNotNull(Platform.valueOf(ruleKind[1].uppercase())) {
"unrecognized platform ${ruleKind[1]}"
},
moduleName = argMap.mandatorySingle(KotlinBuilderFlags.KOTLIN_MODULE_NAME).also {
check(it.isNotBlank()) { "--kotlin_module_name should not be blank" }
},
)
}
@OptIn(ExperimentalPathApi::class)
private fun createJvmTask(
info: CompilationTaskInfo,
workingDir: Path,
args: ArgMap<KotlinBuilderFlags>,
): JvmCompilationTask {
var outJarPath = args.optionalSingle(KotlinBuilderFlags.OUTPUT)
val generatedKspSrcJar = args.optionalSingle(KotlinBuilderFlags.KSP_GENERATED_JAVA_SRCJAR)
if (outJarPath == null) {
outJarPath = generatedKspSrcJar!!
}
val outJar = workingDir.resolve(outJarPath)
//val kotlincDir = jar.parent.resolve("_kotlinc")
//Files.createDirectories(kotlincDir)
val targetName = outJar.fileName.toString().substringBeforeLast(".jar")
//fun resolveAndCreate(part: String): Path {
// val dir = kotlincDir.resolve("$targetName-$part")
// dir.deleteRecursively()
// Files.createDirectory(dir)
// return dir
//}
//fun resolve(part: String): Path {
// return kotlincDir.resolve("$targetName-$part")
//}
//val tempDir = resolve("temp")
fun pathList(list: List<String>): List<Path> {
return list.map { workingDir.resolve(it).toAbsolutePath().normalize() }
}
val root = JvmCompilationTask(
workingDir = workingDir,
args = args,
info = info,
outJar = outJar,
outputs = Outputs(
srcjar = args.optionalSingle(KotlinBuilderFlags.KOTLIN_OUTPUT_SRCJAR)?.let { workingDir.resolve(it) },
jdeps = args.optionalSingle(KotlinBuilderFlags.KOTLIN_OUTPUT_JDEPS)?.let { workingDir.resolve(it) },
abiJar = args.optionalSingle(KotlinBuilderFlags.ABI_JAR)?.let { workingDir.resolve(it) },
),
//directories = Directories(
// //classes = resolveAndCreate("classes"),
// //abiClasses = if (args.has(KotlinBuilderFlags.ABI_JAR)) resolveAndCreate("abi-classes") else null,
//
// //temp = tempDir,
// //incrementalData = resolve("incremental"),
//),
inputs = Inputs(
classpath = pathList(args.mandatory(KotlinBuilderFlags.CLASSPATH)),
depsArtifacts = args.optional(KotlinBuilderFlags.DEPS_ARTIFACTS) ?: emptyList(),
directDependencies = args.mandatory(KotlinBuilderFlags.DIRECT_DEPENDENCIES),
processors = args.optional(KotlinBuilderFlags.PROCESSORS) ?: emptyList(),
processorPaths = args.optional(KotlinBuilderFlags.PROCESSOR_PATH) ?: emptyList(),
stubsPluginOptions = args.optional(KotlinBuilderFlags.STUBS_PLUGIN_OPTIONS) ?: emptyList(),
stubsPluginClasspath = args.optional(KotlinBuilderFlags.STUBS_PLUGIN_CLASSPATH) ?: emptyList(),
compilerPluginClasspath = args.optional(KotlinBuilderFlags.COMPILER_PLUGIN_CLASSPATH)?.let { pathList(it) } ?: emptyList(),
),
)
return root
}

View File

@@ -0,0 +1,43 @@
package org.jetbrains.bazel.jvm.kotlin
internal enum class KotlinBuilderFlags {
TARGET_LABEL,
CLASSPATH,
DIRECT_DEPENDENCIES,
DEPS_ARTIFACTS,
PROCESSOR_PATH,
PROCESSORS,
STUBS_PLUGIN_OPTIONS,
STUBS_PLUGIN_CLASSPATH,
COMPILER_PLUGIN_CLASSPATH,
OUTPUT,
RULE_KIND,
KOTLIN_MODULE_NAME,
API_VERSION,
LANGUAGE_VERSION,
JVM_TARGET,
OPT_IN,
ALLOW_KOTLIN_PACKAGE,
LAMBDAS,
JVM_DEFAULT,
INLINE_CLASSES,
CONTEXT_RECEIVERS,
WARN,
KOTLIN_OUTPUT_SRCJAR,
KOTLIN_FRIEND_PATHS,
KOTLIN_OUTPUT_JDEPS,
KOTLIN_DEBUG_TAGS,
ABI_JAR,
GENERATED_JAVA_SRCJAR,
STRICT_KOTLIN_DEPS,
REDUCED_CLASSPATH_MODE,
INSTRUMENT_COVERAGE,
KSP_GENERATED_JAVA_SRCJAR,
}

View File

@@ -0,0 +1,273 @@
/*
* 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.
*
*/
package org.jetbrains.bazel.jvm.kotlin
import com.google.devtools.build.lib.view.proto.Deps
import com.google.devtools.build.lib.view.proto.Deps.Dependencies
import com.google.devtools.build.runfiles.Runfiles
import org.jetbrains.kotlin.buildtools.api.*
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.compilerRunner.toArgumentStrings
import org.jetbrains.kotlin.config.Services
import java.io.*
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import kotlin.jvm.java
private val runFiles by lazy {
Runfiles.preload().unmapped()
}
private fun resolveVerifiedFromProperty(key: String): String {
val path = (System.getProperty(key) ?: throw FileNotFoundException("no reference for $key in ${System.getProperties()}"))
return path.splitToSequence(',').map { runFiles.rlocation(it) }.joinToString(",")
}
internal fun doCompileKotlin(
task: JvmCompilationTask,
context: CompilationTaskContext,
sources: List<File>,
): Int {
val args = K2JVMCompilerArguments()
args.noStdlib = true
args.classpath = createClasspath(task)
args.apiVersion = task.args.optionalSingle(KotlinBuilderFlags.API_VERSION)
args.languageVersion = task.args.optionalSingle(KotlinBuilderFlags.LANGUAGE_VERSION)
args.jvmTarget = task.args.mandatorySingle(KotlinBuilderFlags.JVM_TARGET)
args.moduleName = task.info.moduleName
args.disableStandardScript = true
// kotlin bug - not compatible with a new X compiler-plugin syntax
//compilationArgs.disableDefaultScriptingPlugin = true
task.args.optional(KotlinBuilderFlags.KOTLIN_FRIEND_PATHS)?.let { value ->
args.friendPaths = value.map { task.workingDir.resolve(it).toString() }.toTypedArray()
}
args.destination = task.outJar.toString()
task.args.optional(KotlinBuilderFlags.OPT_IN)?.let {
args.optIn = it.toTypedArray()
}
if (task.args.boolFlag(KotlinBuilderFlags.ALLOW_KOTLIN_PACKAGE)) {
args.allowKotlinPackage = true
}
task.args.optionalSingle(KotlinBuilderFlags.LAMBDAS)?.let {
args.lambdas = it
}
task.args.optionalSingle(KotlinBuilderFlags.JVM_DEFAULT)?.let {
args.jvmDefault = it
}
if (task.args.boolFlag(KotlinBuilderFlags.INLINE_CLASSES)) {
args.inlineClasses = true
}
if (task.args.boolFlag(KotlinBuilderFlags.CONTEXT_RECEIVERS)) {
args.contextReceivers = true
}
task.args.optionalSingle(KotlinBuilderFlags.WARN)?.let {
when (it) {
"off" -> args.suppressWarnings = true
"error" -> args.allWarningsAsErrors = true
else -> throw IllegalArgumentException("unsupported kotlinc warning option: $it")
}
}
configurePlugins(args = args, task = task)
if (context.isTracing) {
val label = task.info.label
context.out.appendLine("\u001B[1m=============== K2JVM Compiler Arguments ($label) ===============\u001B[0m")
context.out.appendLine(args.toArgumentStrings().joinToString("\n"))
context.out.appendLine("\u001B[1m=============== END of K2JVM Compiler Arguments ($label) ===============\u001B[0m")
}
val compiler = K2JVMCompiler()
require(args.freeArgs.isEmpty())
args.freeArgs = sources.map { it.absolutePath }
val logCollector = WriterBackedMessageCollector(verbose = context.isTracing)
val result = compiler.exec(
messageCollector = logCollector,
services = Services.EMPTY,
arguments = args,
)
if (result.code != 0 || context.isTracing) {
for (entry in logCollector.entries) {
context.out.appendLine(MessageRenderer.PLAIN_RELATIVE_PATHS.render(entry.severity, entry.message, entry.location))
}
}
return result.code
}
//internal fun packOutput(
// task: JvmCompilationTask,
// context: CompilationTaskContext,
//) {
// val outputs = task.outputs
// if (outputs.jar != null) {
// context.execute("create jar") {
// val packageIndexBuilder = PackageIndexBuilder()
// ZipArchiveOutputStream(
// channel = FileChannel.open(task.outputs.jar, W_OVERWRITE),
// zipIndexWriter = ZipIndexWriter(indexWriter = packageIndexBuilder.indexWriter),
// ).use { out ->
// JarCreator(
// packageIndexBuilder = packageIndexBuilder,
// targetLabel = task.info.label,
// injectingRuleKind = "kt_${task.info.ruleKind.name.lowercase()}",
// out = out,
// ).use {
// it.addDirectory(task.directories.classes)
// }
// }
// }
// }
//}
internal fun createClasspath(task: JvmCompilationTask): String {
if (!task.args.optionalSingle(KotlinBuilderFlags.REDUCED_CLASSPATH_MODE).toBoolean()) {
return task.inputs.classpath.joinToString(File.pathSeparator) { it.toString() }
}
val transitiveDepsForCompile = LinkedHashSet<String>()
for (jdepsPath in task.inputs.depsArtifacts) {
BufferedInputStream(Files.newInputStream(Path.of(jdepsPath))).use {
val deps = Dependencies.parseFrom(it)
for (dep in deps.dependencyList) {
if (dep.kind == Deps.Dependency.Kind.EXPLICIT) {
transitiveDepsForCompile.add(dep.path)
}
}
}
}
return (task.inputs.directDependencies.asSequence() + transitiveDepsForCompile)
.joinToString(File.pathSeparator)
}
private fun configurePlugins(
task: JvmCompilationTask,
args: K2JVMCompilerArguments,
) {
val pluginConfigurations = mutableListOf<String>()
// put user plugins first
for (path in task.inputs.compilerPluginClasspath) {
pluginConfigurations.add(path.toString())
}
val outputs = task.outputs
outputs.jdeps?.let {
var s = "${resolveVerifiedFromProperty("kotlin.bazel.jdeps.plugin")}=output=${outputs.jdeps},target_label=${task.info.label}"
task.args.optionalSingle(KotlinBuilderFlags.STRICT_KOTLIN_DEPS)?.let {
s += ",strict_kotlin_deps=$it"
}
pluginConfigurations.add(s)
}
outputs.abiJar?.let {
pluginConfigurations.add("${resolveVerifiedFromProperty("kotlin.bazel.abi.plugin")}=outputDir=${outputs.abiJar},removePrivateClasses=true")
}
if (pluginConfigurations.isNotEmpty()) {
args.pluginConfigurations = pluginConfigurations.toTypedArray()
}
}
private val kotlinProjectId = ProjectId.ProjectUUID(UUID.randomUUID())
@Suppress("unused")
@OptIn(ExperimentalBuildToolsApi::class)
private fun incrementalCompilation(context: CompilationTaskContext, sources: List<File>, args: List<String>) {
val service = CompilationService.loadImplementation(context::class.java.classLoader)
val strategyConfig = service.makeCompilerExecutionStrategyConfiguration()
val compilationConfig = service
.makeJvmCompilationConfiguration()
.useLogger(WorkerKotlinLogger(out = context.out, isDebugEnabled = context.isTracing))
val result = service.compileJvm(
projectId = kotlinProjectId,
strategyConfig = strategyConfig,
compilationConfig = compilationConfig,
sources = sources,
arguments = args,
)
if (result != CompilationResult.COMPILATION_SUCCESS) {
throw CompilationStatusException("compile phase failed", result.ordinal)
}
}
private data class LogMessage(
@JvmField val severity: CompilerMessageSeverity,
@JvmField val message: String,
@JvmField val location: CompilerMessageSourceLocation?,
)
private class WriterBackedMessageCollector(
private val verbose: Boolean,
) : MessageCollector {
private var hasErrors = false
@JvmField val entries = mutableListOf<LogMessage>()
override fun clear() {
}
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
if (!verbose && CompilerMessageSeverity.Companion.VERBOSE.contains(severity)) {
return
}
hasErrors = hasErrors or severity.isError
entries.add(LogMessage(severity = severity, message = message, location = location))
}
override fun hasErrors(): Boolean = hasErrors
}
private class WorkerKotlinLogger(private val out: Writer, override val isDebugEnabled: Boolean) : KotlinLogger {
override fun error(msg: String, throwable: Throwable?) {
out.appendLine(msg)
throwable?.let {
PrintWriter(out).use {
throwable.printStackTrace(it)
}
}
}
override fun warn(msg: String) {
out.append("WARN: ").appendLine(msg)
}
override fun info(msg: String) {
out.append("INFO: ").appendLine(msg)
}
override fun debug(msg: String) {
if (isDebugEnabled) {
out.append("DEBUG: ").appendLine(msg)
}
}
override fun lifecycle(msg: String) {
out.appendLine(msg)
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 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.
*/
package org.jetbrains.bazel.jvm.kotlin
internal sealed class KotlinToolException(
msg: String,
ex: Throwable? = null,
) : RuntimeException(msg, ex)
internal class CompilationException(
msg: String,
cause: Throwable? = null,
) : KotlinToolException(msg, cause)
internal class CompilationStatusException(
msg: String,
val status: Int,
) : KotlinToolException(msg)

View File

@@ -0,0 +1,55 @@
package org.jetbrains.bazel.jvm.kotlin
import java.nio.file.Path
enum class RuleKind {
LIBRARY,
BINARY,
TEST,
IMPORT
}
enum class Platform {
JVM, UNRECOGNIZED
}
data class CompilationTaskInfo(
@JvmField val label: String,
@JvmField val platform: Platform,
@JvmField val ruleKind: RuleKind,
@JvmField val moduleName: String,
)
internal data class JvmCompilationTask(
@JvmField val workingDir: Path,
@JvmField val args: ArgMap<KotlinBuilderFlags>,
@JvmField val info: CompilationTaskInfo,
@JvmField val outputs: Outputs,
@JvmField val inputs: Inputs,
@JvmField val outJar: Path,
)
data class Outputs(
@JvmField val jdeps: Path?,
@JvmField val srcjar: Path?,
@JvmField val abiJar: Path?,
)
data class Inputs(
@JvmField val classpath: List<Path>,
@JvmField val directDependencies: List<String>,
@JvmField val processors: List<String>,
@JvmField val processorPaths: List<String>,
@JvmField val stubsPluginOptions: List<String>,
@JvmField val stubsPlugins: List<String> = emptyList(),
@JvmField val stubsPluginClasspath: List<String>,
@JvmField val compilerPlugins: List<String> = emptyList(),
@JvmField val compilerPluginClasspath: List<Path>,
@JvmField val javacFlags: List<String> = emptyList(),
@JvmField val depsArtifacts: List<String>,
)

View File

@@ -0,0 +1,19 @@
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_import", "kt_jvm_library")
load("//:jvm.bzl", "jvm_resources")
kt_jvm_library(
name = "abi",
srcs = glob(["**/*.kt"]),
deps = [
"//:kotlin-metadata-jvm",
"//:kotlin-compiler",
],
runtime_deps = [":resources"],
visibility = ["//src/kotlin-builder:__pkg__"],
)
jvm_resources(
name = "resources",
files = glob(["META-INF/**/*"]),
visibility = ["//src/kotlin-builder:__pkg__"],
)

View File

@@ -0,0 +1,262 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
import org.jetbrains.kotlin.backend.jvm.extensions.ClassGenerator
import org.jetbrains.kotlin.backend.jvm.extensions.ClassGeneratorExtension
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.codegen.`when`.WhenByEnumsMapping
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
import org.jetbrains.kotlin.ir.util.isFileClass
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.FieldVisitor
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import kotlin.metadata.jvm.JvmFieldSignature
import kotlin.metadata.jvm.JvmMemberSignature
import kotlin.metadata.jvm.JvmMethodSignature
enum class AbiMethodInfo {
KEEP,
STRIP,
}
sealed class AbiClassInfo {
object Public : AbiClassInfo()
class Stripped(val memberInfo: Map<JvmMemberSignature, AbiMethodInfo>, val prune: Boolean = false) : AbiClassInfo()
object Deleted : AbiClassInfo()
}
/**
* Record ABI information for all classes in the current compilation unit.
*
* This needs to be a `ClassBuilderInterceptor`, since we need the descriptors
* in order to know which methods can be safely stripped from the output.
* On the other hand we cannot produce any output in this pass, since we need
* to know which classes are part of the public ABI in order to produce the
* correct `InnerClasses` attributes.
*
* ---
*
* Classes which are private, local, or anonymous can be stripped unless
* they are marked as part of the public ABI by a bit in the Kotlin
* metadata annotation. Public ABI classes have to be kept verbatim, since
* they are copied to all call sites of a surrounding inline function.
*
* If we keep a class we will strip the bodies from all non-inline function,
* remove private functions, and copy inline functions verbatim. There is one
* exception to this for inline suspend functions.
*
* For an inline suspend function `f` we will usually generate two methods,
* a non-inline method `f` and an inline method `f$$forInline`. The former can
* be stripped. However, if `f` is not callable directly, we only generate a
* single inline method `f` which should be kept.
*/
class JvmAbiClassBuilderInterceptor(
private val removeDataClassCopyIfConstructorIsPrivate: Boolean,
private val removePrivateClasses: Boolean,
private val treatInternalAsPrivate: Boolean,
) : ClassGeneratorExtension {
private var abiClassInfoBuilder = JvmAbiClassInfoBuilder(removePrivateClasses)
fun buildAbiClassInfoAndReleaseResources(): Map<String, AbiClassInfo> {
return abiClassInfoBuilder.buildClassInfo().also {
abiClassInfoBuilder = JvmAbiClassInfoBuilder(removePrivateClasses)
}
}
override fun generateClass(generator: ClassGenerator, declaration: IrClass?): ClassGenerator =
AbiInfoClassGenerator(generator, declaration)
private inner class AbiInfoClassGenerator(
private val delegate: ClassGenerator,
irClass: IrClass?,
) : ClassGenerator by delegate {
private val isPrivateClass = irClass != null && DescriptorVisibilities.isPrivate(irClass.visibility)
private val isDataClass = irClass != null && irClass.isData
private val removeClassFromAbi = shouldRemoveFromAbi(irClass, removePrivateClasses, treatInternalAsPrivate)
@OptIn(UnsafeDuringIrConstructionAPI::class)
private val primaryConstructorIsNotInAbi = irClass
?.primaryConstructor
?.visibility
?.let {
DescriptorVisibilities.isPrivate(it) || (treatInternalAsPrivate && it == DescriptorVisibilities.INTERNAL)
} == true
lateinit var internalName: String
lateinit var superInterfaces: List<String>
var localOrAnonymousClass = false
var keepClassAsIs = false
val memberInfos = mutableMapOf<JvmMemberSignature, AbiMethodInfo>()
val maskedMethods = mutableSetOf<JvmMethodSignature>() // Methods which should be stripped even if they are marked as KEEP
override fun defineClass(
version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<out String>
) {
// Always keep annotation classes
// TODO: Investigate whether there are cases where we can remove annotation classes from the ABI.
keepClassAsIs = keepClassAsIs || access and Opcodes.ACC_ANNOTATION != 0
internalName = name
superInterfaces = interfaces.asList()
delegate.defineClass(version, access, name, signature, superName, interfaces)
}
override fun visitEnclosingMethod(owner: String, name: String?, desc: String?) {
localOrAnonymousClass = true
delegate.visitEnclosingMethod(owner, name, desc)
}
override fun newField(
declaration: IrField?, access: Int, name: String, desc: String, signature: String?, value: Any?
): FieldVisitor {
val field = delegate.newField(declaration, access, name, desc, signature, value)
if (keepClassAsIs || removeClassFromAbi) {
// We don't care about fields when we remove or keep this class completely.
return field
}
val visibility = declaration?.visibility ?: DescriptorVisibilities.DEFAULT_VISIBILITY
if (DescriptorVisibilities.isPrivate(visibility)) {
// Remove all private fields.
return field
}
if (treatInternalAsPrivate && visibility == DescriptorVisibilities.INTERNAL) {
// Remove all internal fields.
return field
}
// Keep otherwise.
memberInfos[JvmFieldSignature(name, desc)] = AbiMethodInfo.KEEP
return field
}
override fun newMethod(
declaration: IrFunction?, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
): MethodVisitor {
val method = delegate.newMethod(declaration, access, name, desc, signature, exceptions)
if (keepClassAsIs || removeClassFromAbi) {
// We don't care about methods when we remove or keep this class completely.
return method
}
// inline suspend functions are a special case: Unless they use reified type parameters,
// we will transform the original method and generate a $$forInline method for the inliner.
// Only the latter needs to be kept, the former can be stripped. Unfortunately, there is no
// metadata to indicate this (the inliner simply first checks for a method such as `f$$forInline`
// and then checks for `f` if this method doesn't exist) so we have to remember to strip the
// original methods if there was a $$forInline version.
if (name.endsWith(FOR_INLINE_SUFFIX) && !isPrivateClass) {
memberInfos[JvmMethodSignature(name, desc)] = AbiMethodInfo.KEEP
maskedMethods += JvmMethodSignature(name.removeSuffix(FOR_INLINE_SUFFIX), desc)
return method
}
// Remove private functions from the ABI jars
if (
access and Opcodes.ACC_PRIVATE != 0 && declaration != null && DescriptorVisibilities.isPrivate(declaration.visibility)
|| name == "<clinit>" || name.startsWith("access\$") && access and Opcodes.ACC_SYNTHETIC != 0
) {
return method
}
// Remove internal functions from the ABI jars
if (treatInternalAsPrivate && declaration?.visibility == DescriptorVisibilities.INTERNAL) {
return method
}
if (isDataClass && primaryConstructorIsNotInAbi) {
val removeCopy = when (name) {
"copy" -> removeDataClassCopyIfConstructorIsPrivate
"copy${JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX}" ->
removeDataClassCopyIfConstructorIsPrivate || access and Opcodes.ACC_PUBLIC == 0
else -> false
}
if (removeCopy) return method
}
// Copy inline functions verbatim
if (declaration?.isInline == true && !isPrivateClass) {
memberInfos[JvmMethodSignature(name, desc)] = AbiMethodInfo.KEEP
} else {
memberInfos[JvmMethodSignature(name, desc)] = AbiMethodInfo.STRIP
}
return method
}
// Parse the public ABI flag from the Kotlin metadata annotation
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor {
val delegate = delegate.visitAnnotation(desc, visible)
if (keepClassAsIs || desc != JvmAnnotationNames.METADATA_DESC)
return delegate
return object : AnnotationVisitor(Opcodes.API_VERSION, delegate) {
override fun visit(name: String?, value: Any?) {
if ((name == JvmAnnotationNames.METADATA_EXTRA_INT_FIELD_NAME) && (value is Int)) {
keepClassAsIs = keepClassAsIs || value and JvmAnnotationNames.METADATA_PUBLIC_ABI_FLAG != 0
}
super.visit(name, value)
}
}
}
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
abiClassInfoBuilder.addInnerClass(name, outerName)
delegate.visitInnerClass(name, outerName, innerName, access)
}
override fun done(generateSmapCopyToAnnotation: Boolean) {
// Remove local or anonymous classes unless they are in the scope of an inline function and
// strip non-inline methods from all other classes.
val classInfo = when {
keepClassAsIs -> AbiClassInfo.Public
removeClassFromAbi -> AbiClassInfo.Deleted
localOrAnonymousClass -> AbiClassInfo.Deleted
isWhenMappingClass -> AbiClassInfo.Deleted
else -> {
for (method in maskedMethods) {
memberInfos[method] = AbiMethodInfo.STRIP
}
AbiClassInfo.Stripped(memberInfos)
}
}
abiClassInfoBuilder.recordInitialClassInfo(internalName, classInfo, superInterfaces)
delegate.done(generateSmapCopyToAnnotation)
}
private val isWhenMappingClass: Boolean
get() = internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX)
}
}
private fun shouldRemoveFromAbi(irClass: IrClass?, removePrivateClasses: Boolean, treatInternalAsPrivate: Boolean): Boolean = when {
irClass == null -> false
irClass.isFileClass -> false
removePrivateClasses -> irClass.isVisibilityStrippedFromAbi(stripInternal = treatInternalAsPrivate)
else -> false
}
private fun IrDeclarationWithVisibility.isVisibilityStrippedFromAbi(stripInternal: Boolean): Boolean {
val isInAbi = visibility == DescriptorVisibilities.PUBLIC
|| visibility == DescriptorVisibilities.PROTECTED
|| (!stripInternal && visibility == DescriptorVisibilities.INTERNAL)
return !isInAbi || parentClassOrNull?.isVisibilityStrippedFromAbi(stripInternal) == true
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
/**
* Collects information about kept/stripped/deleted classes, and then adapts it so that it would be consistent if private interfaces
* are present in the module. The latter is done only if [removePrivateClasses] is enabled.
*
* When using this class, call [recordInitialClassInfo]/[addInnerClass] repeatedly until all necessary data is collected, and then call
* [buildClassInfo] to get the resulting class infos.
*
* Private interface is a special case from the ABI standpoint because it is allowed to expose private interface by inheriting a public
* class from it, see KT-20088. This code keeps all private interfaces which are implemented (perhaps indirectly) by at least one public
* (in terms of ABI) class in the module. Also, in case such private interface is nested, all its outer classes should be kept but pruned.
*/
internal class JvmAbiClassInfoBuilder(private val removePrivateClasses: Boolean) {
private val abiClassInfo = mutableMapOf<String, AbiClassInfo>()
private val innerClassToOuter = mutableMapOf<String, String>()
private val allSuperInterfaces = mutableMapOf<String, List<String>>()
fun recordInitialClassInfo(className: String, info: AbiClassInfo, superInterfaces: List<String>) {
abiClassInfo[className] = info
if (removePrivateClasses) {
allSuperInterfaces[className] = superInterfaces
}
}
fun addInnerClass(innerClass: String, outerClass: String?) {
if (outerClass == null) return
if (removePrivateClasses) {
innerClassToOuter.getOrPut(innerClass) { outerClass }
}
}
fun buildClassInfo(): Map<String, AbiClassInfo> {
if (removePrivateClasses) {
for ((className, info) in abiClassInfo.toMap()) {
if (info != AbiClassInfo.Deleted) {
keepInterfacesOf(className)
}
}
}
return abiClassInfo
}
private fun keepInterfacesOf(className: String) {
for (superInterface in allSuperInterfaces[className].orEmpty()) {
if (abiClassInfo.put(superInterface, AbiClassInfo.Public) == AbiClassInfo.Public)
continue
for (outerClass in generateSequence(superInterface, innerClassToOuter::get).drop(1)) {
if (abiClassInfo[outerClass] == AbiClassInfo.Deleted) {
abiClassInfo[outerClass] = AbiClassInfo.Stripped(emptyMap(), prune = true)
}
}
keepInterfacesOf(superInterface)
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
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.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
@OptIn(ExperimentalCompilerApi::class)
class JvmAbiCommandLineProcessor : CommandLineProcessor {
companion object {
const val COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.jvm.abi"
val OUTPUT_PATH_OPTION: CliOption =
CliOption(
"outputDir",
"<path>",
"Output path for generated files. This can be either a directory or a jar file.",
true
)
val REMOVE_DEBUG_INFO_OPTION: CliOption =
CliOption(
"removeDebugInfo",
"true/false",
"Remove debug info from the generated class files. False by default. Note that if ABI jars are used for incremental " +
"compilation, it's not safe to remove debug info because debugger will behave incorrectly on non-recompiled call " +
"sites of inline functions.",
false,
)
val REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION: CliOption =
CliOption(
"removeDataClassCopyIfConstructorIsPrivate",
"true/false",
"Remove the 'copy' function from ABI if the primary constructor of the data class is private. False by default. " +
"Note that if ABI jars are used for incremental compilation, removal of the 'copy' function might break usages " +
"outside of the compilation unit where it's declared.",
false,
)
val PRESERVE_DECLARATION_ORDER_OPTION: CliOption =
CliOption(
"preserveDeclarationOrder",
"true/false",
"Do not sort class members in output abi.jar. Legacy flag that allows tools that rely on methods/fields order in the source" +
" to keep working. Flipping this to true reduces stability of the ABI.",
false,
)
val REMOVE_PRIVATE_CLASSES_OPTION: CliOption =
CliOption(
"removePrivateClasses",
"true/false",
"Remove private classes from ABI. False by default due to backwards compatibility. If enabled, " +
"private top-level classes will no longer be available from Java classes in the same package.",
false,
)
val TREAT_INTERNAL_AS_PRIVATE_OPTION: CliOption =
CliOption(
"treatInternalAsPrivate",
"true/false",
"""Treat internal declarations as private and remove from ABI. False by default due to backwards compatibility.
|If enabled, internal functions are removed and will no longer be available from Java if it's compiled against abi.jar.
|Works best in conjunction with flags:
| * removePrivateClasses - internal classes are being removed too;
| * removeDataClassCopyIfConstructorIsPrivate - copy method is removed along with internal constructor.""".trimMargin(),
false,
)
}
override val pluginId: String
get() = COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption>
get() = listOf(
OUTPUT_PATH_OPTION,
REMOVE_DEBUG_INFO_OPTION,
REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION,
PRESERVE_DECLARATION_ORDER_OPTION,
REMOVE_PRIVATE_CLASSES_OPTION,
TREAT_INTERNAL_AS_PRIVATE_OPTION,
)
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
OUTPUT_PATH_OPTION -> configuration.put(JvmAbiConfigurationKeys.OUTPUT_PATH, value)
REMOVE_DEBUG_INFO_OPTION -> configuration.put(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO, value == "true")
REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION -> configuration.put(
JvmAbiConfigurationKeys.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE,
value == "true"
)
PRESERVE_DECLARATION_ORDER_OPTION -> configuration.put(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER, value == "true")
REMOVE_PRIVATE_CLASSES_OPTION -> configuration.put(
JvmAbiConfigurationKeys.REMOVE_PRIVATE_CLASSES,
value == "true"
)
TREAT_INTERNAL_AS_PRIVATE_OPTION -> configuration.put(
JvmAbiConfigurationKeys.TREAT_INTERNAL_AS_PRIVATE,
value == "true"
)
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
import org.jetbrains.kotlin.backend.jvm.extensions.ClassGeneratorExtension
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.messageCollector
import java.io.File
@OptIn(ExperimentalCompilerApi::class)
class JvmAbiComponentRegistrar : CompilerPluginRegistrar() {
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
val outputPath = configuration.getNotNull(JvmAbiConfigurationKeys.OUTPUT_PATH)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
val removeDataClassCopy = configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE)
val builderExtension = JvmAbiClassBuilderInterceptor(
removeDataClassCopy,
configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_PRIVATE_CLASSES),
configuration.getBoolean(JvmAbiConfigurationKeys.TREAT_INTERNAL_AS_PRIVATE),
)
val outputExtension = JvmAbiOutputExtension(
File(outputPath), builderExtension::buildAbiClassInfoAndReleaseResources,
configuration.messageCollector, configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO),
removeDataClassCopy,
configuration.getBoolean(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER),
configuration.getBoolean(JvmAbiConfigurationKeys.TREAT_INTERNAL_AS_PRIVATE),
)
ClassGeneratorExtension.registerExtension(builderExtension)
ClassFileFactoryFinalizerExtension.registerExtension(outputExtension)
}
override val supportsK2: Boolean
get() = true
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
import org.jetbrains.kotlin.config.CompilerConfigurationKey
object JvmAbiConfigurationKeys {
val OUTPUT_PATH: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.OUTPUT_PATH_OPTION.description)
val REMOVE_DEBUG_INFO: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.description)
val REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.description)
val PRESERVE_DECLARATION_ORDER: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.PRESERVE_DECLARATION_ORDER_OPTION.description)
val REMOVE_PRIVATE_CLASSES: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_PRIVATE_CLASSES_OPTION.description)
val TREAT_INTERNAL_AS_PRIVATE: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.TREAT_INTERNAL_AS_PRIVATE_OPTION.description)
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
import kotlin.metadata.*
import kotlin.metadata.jvm.*
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.*
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
/**
* Wrap the visitor for a Kotlin Metadata annotation to strip out private and local
* functions, properties, and type aliases as well as local delegated properties.
*/
fun abiMetadataProcessor(
annotationVisitor: AnnotationVisitor,
removeDataClassCopyIfConstructorIsPrivate: Boolean,
preserveDeclarationOrder: Boolean,
classesToBeDeleted: Set<String>,
pruneClass: Boolean,
treatInternalAsPrivate: Boolean,
): AnnotationVisitor =
kotlinClassHeaderVisitor { header ->
// kotlinx-metadata only supports writing Kotlin metadata of version >= 1.4, so we need to
// update the metadata version if we encounter older metadata annotations.
val metadataVersion = header.metadataVersion.takeIf { v ->
val major = v.getOrNull(0) ?: 0
val minor = v.getOrNull(1) ?: 0
major > 1 || major == 1 && minor >= 4
} ?: intArrayOf(1, 4)
val newHeader = runCatching {
KotlinClassMetadata.transform(header) { metadata ->
when (metadata) {
is KotlinClassMetadata.Class -> {
metadata.kmClass.removePrivateDeclarations(
removeDataClassCopyIfConstructorIsPrivate,
preserveDeclarationOrder,
classesToBeDeleted,
pruneClass,
treatInternalAsPrivate,
)
}
is KotlinClassMetadata.FileFacade -> {
metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder, pruneClass, treatInternalAsPrivate)
}
is KotlinClassMetadata.MultiFileClassPart -> {
metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder, pruneClass, treatInternalAsPrivate)
}
else -> Unit
}
}
}.getOrElse { cause ->
// TODO: maybe jvm-abi-gen should throw this exception by default, and not only in tests.
if (System.getProperty("idea.is.unit.test").toBoolean()) {
val actual = "${metadataVersion[0]}.${metadataVersion[1]}"
val expected = JvmMetadataVersion.LATEST_STABLE_SUPPORTED.toString()
throw AssertionError(
"jvm-abi-gen can't process class file with the new metadata version because the version of kotlinx-metadata-jvm " +
"it depends on is too old.\n" +
"Class file has metadata version $actual, but default metadata version of kotlinx-metadata-jvm is " +
"$expected, so it can process class files with metadata version up to +1 from that (because of " +
"Kotlin/JVM's one-version forward compatibility policy).\n" +
"To fix this error, ensure that jvm-abi-gen depends on the latest version of kotlinx-metadata-jvm.\n" +
"If this happens during the update of the default language version in the project, make sure that " +
"a version of kotlinx-metadata-jvm has been published that supports this version, and update " +
"\"versions.kotlinx-metadata-jvm\" in `gradle/versions.properties`.",
cause
)
}
header
}
// Write out the stripped annotation
annotationVisitor.visitKotlinMetadata(newHeader)
}
/**
* Parse a KotlinClassHeader from an existing Kotlin Metadata annotation visitor.
*/
private fun kotlinClassHeaderVisitor(body: (Metadata) -> Unit): AnnotationVisitor =
object : AnnotationVisitor(Opcodes.API_VERSION) {
var kind: Int = 1
var metadataVersion: IntArray = intArrayOf()
var data1: MutableList<String> = mutableListOf()
var data2: MutableList<String> = mutableListOf()
var extraString: String? = null
var packageName: String? = null
var extraInt: Int = 0
override fun visit(name: String, value: Any?) {
when (name) {
KIND_FIELD_NAME -> kind = value as Int
METADATA_EXTRA_INT_FIELD_NAME -> extraInt = value as Int
METADATA_VERSION_FIELD_NAME -> metadataVersion = value as IntArray
METADATA_EXTRA_STRING_FIELD_NAME -> extraString = value as String
METADATA_PACKAGE_NAME_FIELD_NAME -> packageName = value as String
}
}
override fun visitArray(name: String): AnnotationVisitor? {
val destination = when (name) {
METADATA_DATA_FIELD_NAME -> data1
METADATA_STRINGS_FIELD_NAME -> data2
else -> return null
}
return object : AnnotationVisitor(Opcodes.API_VERSION) {
override fun visit(name: String?, value: Any?) {
destination += value as String
}
}
}
override fun visitEnd() {
body(
Metadata(
kind,
metadataVersion,
data1.toTypedArray(),
data2.toTypedArray(),
extraString,
packageName,
extraInt
)
)
}
}
/**
* Serialize a KotlinClassHeader to an existing Kotlin Metadata annotation visitor.
*/
private fun AnnotationVisitor.visitKotlinMetadata(header: Metadata) {
visit(KIND_FIELD_NAME, header.kind)
visit(METADATA_VERSION_FIELD_NAME, header.metadataVersion)
if (header.data1.isNotEmpty()) {
visitArray(METADATA_DATA_FIELD_NAME).apply {
header.data1.forEach { visit(null, it) }
visitEnd()
}
}
if (header.data2.isNotEmpty()) {
visitArray(METADATA_STRINGS_FIELD_NAME).apply {
header.data2.forEach { visit(null, it) }
visitEnd()
}
}
if (header.extraString.isNotEmpty()) {
visit(METADATA_EXTRA_STRING_FIELD_NAME, header.extraString)
}
if (header.packageName.isNotEmpty()) {
visit(METADATA_PACKAGE_NAME_FIELD_NAME, header.packageName)
}
if (header.extraInt != 0) {
visit(METADATA_EXTRA_INT_FIELD_NAME, header.extraInt)
}
visitEnd()
}
private fun KmClass.removePrivateDeclarations(
removeCopyAlongWithConstructor: Boolean,
preserveDeclarationOrder: Boolean,
classesToBeDeleted: Set<String>,
pruneClass: Boolean,
treatInternalAsPrivate: Boolean,
) {
constructors.removeIf { pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate) }
(this as KmDeclarationContainer).removePrivateDeclarations(
copyFunShouldBeDeleted(removeCopyAlongWithConstructor),
preserveDeclarationOrder,
pruneClass,
treatInternalAsPrivate,
)
nestedClasses.removeIf { "$name\$$it" in classesToBeDeleted }
companionObject = companionObject.takeUnless { "$name\$$it" in classesToBeDeleted }
localDelegatedProperties.clear()
// TODO: do not serialize private type aliases once KT-17229 is fixed.
}
private fun KmPackage.removePrivateDeclarations(
preserveDeclarationOrder: Boolean,
pruneClass: Boolean,
treatInternalAsPrivate: Boolean,
) {
(this as KmDeclarationContainer).removePrivateDeclarations(false, preserveDeclarationOrder, pruneClass, treatInternalAsPrivate)
localDelegatedProperties.clear()
// TODO: do not serialize private type aliases once KT-17229 is fixed.
}
private fun KmDeclarationContainer.removePrivateDeclarations(
copyFunShouldBeDeleted: Boolean,
preserveDeclarationOrder: Boolean,
pruneClass: Boolean,
treatInternalAsPrivate: Boolean,
) {
functions.removeIf { pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate) || (copyFunShouldBeDeleted && it.name == "copy") }
properties.removeIf { pruneClass || it.visibility.shouldRemove(treatInternalAsPrivate) }
if (!preserveDeclarationOrder) {
functions.sortWith(compareBy(KmFunction::name, { it.signature.toString() }))
properties.sortWith(compareBy(KmProperty::name, { it.getterSignature.toString() }))
}
for (property in properties) {
// Whether or not the *non-const* property is initialized by a compile-time constant is not a part of the ABI.
if (!property.isConst) {
property.hasConstant = false
}
}
}
private fun KmClass.copyFunShouldBeDeleted(removeDataClassCopy: Boolean): Boolean =
removeDataClassCopy && isData && constructors.none { !it.isSecondary }
private fun Visibility.shouldRemove(treatInternalAsPrivate: Boolean): Boolean {
return this == Visibility.PRIVATE ||
this == Visibility.PRIVATE_TO_THIS ||
this == Visibility.LOCAL ||
(treatInternalAsPrivate && this == Visibility.INTERNAL)
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.jvm.abi
import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.backend.common.output.SimpleOutputBinaryFile
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.output.writeAllTo
import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.visitWithSplitting
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.ClassRemapper
import org.jetbrains.org.objectweb.asm.commons.Remapper
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.File
import kotlin.metadata.jvm.JvmFieldSignature
import kotlin.metadata.jvm.JvmMethodSignature
class JvmAbiOutputExtension(
private val outputPath: File,
private val abiClassInfoBuilder: () -> Map<String, AbiClassInfo>,
private val messageCollector: MessageCollector,
private val removeDebugInfo: Boolean,
private val removeDataClassCopyIfConstructorIsPrivate: Boolean,
private val preserveDeclarationOrder: Boolean,
private val treatInternalAsPrivate: Boolean,
) : ClassFileFactoryFinalizerExtension {
override fun finalizeClassFactory(factory: ClassFileFactory) {
// We need to wait until the end to produce any output in order to strip classes
// from the InnerClasses attributes.
val outputFiles =
AbiOutputFiles(
abiClassInfoBuilder(),
factory,
removeDebugInfo,
removeDataClassCopyIfConstructorIsPrivate,
preserveDeclarationOrder,
treatInternalAsPrivate,
)
if (outputPath.extension == "jar") {
// We don't include the runtime or main class in interface jars and always reset time stamps.
CompileEnvironmentUtil.writeToJar(
outputPath,
false,
true,
true,
null,
outputFiles,
messageCollector
)
} else {
outputFiles.writeAllTo(outputPath)
}
}
private class InnerClassInfo(val name: String, val outerName: String?, val innerName: String?, val access: Int)
private class AbiOutputFiles(
val abiClassInfos: Map<String, AbiClassInfo>,
val outputFiles: OutputFileCollection,
val removeDebugInfo: Boolean,
val removeCopyAlongWithConstructor: Boolean,
val preserveDeclarationOrder: Boolean,
val treatInternalAsPrivate: Boolean,
) : OutputFileCollection {
private val classesToBeDeleted = abiClassInfos.mapNotNullTo(mutableSetOf()) { (className, action) ->
className.takeIf { action == AbiClassInfo.Deleted }
}
override fun get(relativePath: String): OutputFile? {
error("AbiOutputFiles does not implement `get`.")
}
override fun asList(): List<OutputFile> {
val metadata = outputFiles.asList().filter {
!it.relativePath.endsWith(".class")
}.sortedBy { it.relativePath }
val classFiles = abiClassInfos.keys.sorted().mapNotNull { internalName ->
// Note that outputFile may be null, e.g., for empty $DefaultImpls classes in the JVM backend.
val outputFile = outputFiles.get("$internalName.class") ?: return@mapNotNull null
when (val abiInfo = abiClassInfos.getValue(internalName)) {
is AbiClassInfo.Deleted -> null
is AbiClassInfo.Public -> outputFile // Copy verbatim
is AbiClassInfo.Stripped -> {
val prune = abiInfo.prune
val memberInfo = abiInfo.memberInfo
val innerClassesToKeep = mutableSetOf<String>()
var sourceFile: String? = null
var sourceMap: SourceMapCopier? = null
var sourceMapAnnotationPresent = false
val writer = ClassWriter(0)
val remapper = ClassRemapper(writer, object : Remapper() {
override fun map(internalName: String): String =
internalName.also { innerClassesToKeep.add(it) }
})
val parsingOptions = if (removeDebugInfo) ClassReader.SKIP_DEBUG else 0
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, remapper) {
private val keptFields = mutableListOf<FieldNode>()
private val keptMethods = mutableListOf<MethodNode>()
private val innerClassInfos = mutableMapOf<String, InnerClassInfo>()
override fun visitSource(source: String?, debug: String?) {
sourceFile = source
sourceMap = debug.takeIf { !prune }?.let(SMAPParser::parseOrNull)
?.let { SourceMapCopier(SourceMapper(sourceFile, it), it) }
}
// Strip private fields.
override fun visitField(
access: Int,
name: String,
descriptor: String,
signature: String?,
value: Any?,
): FieldVisitor? {
if (prune || memberInfo[JvmFieldSignature(name, descriptor)] != AbiMethodInfo.KEEP)
return null
return FieldNode(access, name, descriptor, signature, value).also {
keptFields += it
}
}
override fun visitMethod(
access: Int,
name: String,
descriptor: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
if (prune) return null
val info = memberInfo[JvmMethodSignature(name, descriptor)] ?: return null
val node = MethodNode(access, name, descriptor, signature, exceptions).also {
keptMethods += it
}
return if (info != AbiMethodInfo.KEEP && access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) == 0)
BodyStrippingMethodVisitor(node)
else
node
}
// Remove inner classes which are not present in the abi jar.
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
// `visitInnerClass` is called before `visitField`/`visitMethod`, so we don't know
// which types are referenced by kept methods yet.
innerClassInfos[name] = InnerClassInfo(name, outerName, innerName, access)
}
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
if (descriptor == JvmAnnotationNames.SOURCE_DEBUG_EXTENSION_DESC) {
sourceMapAnnotationPresent = true
return null
}
val delegate = super.visitAnnotation(descriptor, visible)
if (descriptor != JvmAnnotationNames.METADATA_DESC)
return delegate
// Strip private declarations from the Kotlin Metadata annotation.
return abiMetadataProcessor(
delegate,
removeCopyAlongWithConstructor,
preserveDeclarationOrder,
classesToBeDeleted,
prune,
treatInternalAsPrivate,
)
}
override fun visitEnd() {
if (!preserveDeclarationOrder) {
// Output class members in sorted order so that changes in original ordering don't affect the ABI JAR.
keptFields.sortWith(compareBy(FieldNode::name, FieldNode::desc))
keptMethods.sortWith(compareBy(MethodNode::name, MethodNode::desc))
}
for (field in keptFields) {
field.accept(cv)
}
for (method in keptMethods) {
val mv = with(method) { cv.visitMethod(access, name, desc, signature, exceptions?.toTypedArray()) }
// Mapping the line numbers should only be done *after* sorting methods, as otherwise the order
// of inline methods may be visible in their synthetic line numbers.
method.accept(sourceMap?.let { SourceMapCopyingMethodVisitor(it, mv) } ?: mv)
}
val sourceMapText = sourceMap?.parent?.takeIf { !it.isTrivial }
?.let { SMAPBuilder.build(it.resultMappings, backwardsCompatibleSyntax = false) }
// This is technically not the right way to use `ClassVisitor` (`visitSource` should be called before
// `visitMethod` and such), but `ClassWriter` doesn't care, and we're a bit constrained here (see above).
cv.visitSource(sourceFile, sourceMapText)
if (sourceMapAnnotationPresent && sourceMapText != null) {
val av = cv.visitAnnotation(JvmAnnotationNames.SOURCE_DEBUG_EXTENSION_DESC, false)
av.visitWithSplitting("value", sourceMapText)
av.visitEnd()
}
innerClassesToKeep.addInnerClasses(innerClassInfos, internalName)
innerClassesToKeep.addOuterClasses(innerClassInfos)
for (name in innerClassesToKeep.sorted()) {
innerClassInfos[name]?.let { cv.visitInnerClass(it.name, it.outerName, it.innerName, it.access) }
}
super.visitEnd()
}
}, parsingOptions)
SimpleOutputBinaryFile(outputFile.sourceFiles, outputFile.relativePath, writer.toByteArray())
}
}
}
return metadata + classFiles
}
// Outer class infos for a class and all classes transitively nested in it (that are public ABI)
// should be kept in its own class file even if the classes are otherwise unused.
private fun MutableSet<String>.addInnerClasses(innerClassInfos: Map<String, InnerClassInfo>, internalName: String) {
val innerClassesByOuterName = innerClassInfos.values.groupBy { it.outerName }
val stack = mutableListOf(internalName)
while (stack.isNotEmpty()) {
val next = stack.removeLast()
add(next)
// Classes form a tree by nesting, so none of the children have been visited yet.
innerClassesByOuterName[next]?.mapNotNullTo(stack) { info ->
info.name.takeUnless { abiClassInfos[it] == AbiClassInfo.Deleted }
}
}
}
// For every class A.B, if its outer class info is kept then so should be A's.
private fun MutableSet<String>.addOuterClasses(innerClassInfos: Map<String, InnerClassInfo>) {
for (name in toList()) {
var info = innerClassInfos[name]
while (info != null) {
info = info.outerName?.takeIf(::add)?.let(innerClassInfos::get)
}
}
}
}
}
private class BodyStrippingMethodVisitor(visitor: MethodVisitor) : MethodVisitor(Opcodes.API_VERSION, visitor) {
override fun visitCode() {
with(mv) {
visitCode()
visitInsn(Opcodes.ACONST_NULL)
visitInsn(Opcodes.ATHROW)
visitMaxs(0, 0)
visitEnd()
}
// Only instructions, frames, try-catch, and locals follow after `visitCode`.
mv = null
}
}

View File

@@ -0,0 +1,6 @@
#
# Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
#
org.jetbrains.kotlin.jvm.abi.JvmAbiCommandLineProcessor

View File

@@ -0,0 +1,6 @@
#
# Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
#
org.jetbrains.kotlin.jvm.abi.JvmAbiComponentRegistrar

View File

@@ -0,0 +1,21 @@
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_import", "kt_jvm_library")
load("//:jvm.bzl", "jvm_resources")
kt_jvm_library(
name = "jdeps",
srcs = glob(["**/*.kt"]),
deps = [
"//src/jar",
"//:kotlin-compiler",
"//:protobuf-java",
"@bazel_tools//src/main/protobuf:deps_java_proto",
],
runtime_deps = [":resources"],
visibility = ["//src/kotlin-builder:__pkg__"],
)
jvm_resources(
name = "resources",
files = glob(["META-INF/**/*"]),
visibility = ["//src/kotlin-builder:__pkg__"],
)

View File

@@ -0,0 +1,140 @@
package io.bazel.kotlin.plugin.jdeps
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.utils.sourceElement
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.forEachType
import org.jetbrains.kotlin.name.ClassId
//private const val JAR_FILE_SEPARATOR = "!/"
private const val ANONYMOUS = "<anonymous>"
internal class ClassUsageRecorder {
@JvmField
val explicitClassesCanonicalPaths: MutableSet<String> = HashSet()
@JvmField
val implicitClassesCanonicalPaths: MutableSet<String> = HashSet()
internal fun recordTypeRef(
typeRef: FirTypeRef,
context: CheckerContext,
isExplicit: Boolean = true,
collectTypeArguments: Boolean = true,
visited: MutableSet<Pair<ClassId, Boolean>> = HashSet(),
) {
recordConeType(
coneKotlinType = typeRef.coneType,
context = context,
isExplicit = isExplicit,
collectTypeArguments = collectTypeArguments,
visited = visited,
)
}
internal fun recordConeType(
coneKotlinType: ConeKotlinType,
context: CheckerContext,
isExplicit: Boolean = true,
collectTypeArguments: Boolean = true,
visited: MutableSet<Pair<ClassId, Boolean>> = HashSet(),
) {
if (collectTypeArguments) {
coneKotlinType.forEachType(
action = { coneType ->
val classId = coneType.classId ?: return@forEachType
if (classId.toString().contains(ANONYMOUS)) {
return@forEachType
}
context.session.symbolProvider
.getClassLikeSymbolByClassId(classId)
?.let {
recordClass(
firClass = it,
context = context,
isExplicit = isExplicit,
collectTypeArguments = collectTypeArguments,
visited = visited
)
}
},
)
}
else {
coneKotlinType.classId?.let { classId ->
if (!classId.isLocal) {
context.session.symbolProvider
.getClassLikeSymbolByClassId(classId)
?.let {
recordClass(
firClass = it,
context = context,
isExplicit = isExplicit,
collectTypeArguments = false,
visited = visited,
)
}
}
}
}
}
internal fun recordClass(
firClass: FirClassLikeSymbol<*>,
context: CheckerContext,
isExplicit: Boolean = true,
collectTypeArguments: Boolean = true,
visited: MutableSet<Pair<ClassId, Boolean>> = HashSet(),
) {
val classIdAndIsExplicit = firClass.classId to isExplicit
if (!visited.add(classIdAndIsExplicit)) {
return
}
firClass.sourceElement?.binaryClass()?.let { addClass(path = it, isExplicit = isExplicit) }
if (firClass is FirClassSymbol<*>) {
for (typeRef in firClass.resolvedSuperTypeRefs) {
recordTypeRef(
typeRef = typeRef,
context = context,
isExplicit = false,
collectTypeArguments = collectTypeArguments,
visited = visited,
)
}
if (collectTypeArguments) {
firClass.typeParameterSymbols
.asSequence()
.flatMap { it.resolvedBounds }
.forEach {
recordTypeRef(
typeRef = it,
context = context,
isExplicit = isExplicit,
collectTypeArguments = true,
visited = visited,
)
}
}
}
}
internal fun addClass(
path: String,
isExplicit: Boolean,
) {
if (isExplicit) {
explicitClassesCanonicalPaths.add(path)
}
else {
implicitClassesCanonicalPaths.add(path)
}
}
}

View File

@@ -0,0 +1,55 @@
package io.bazel.kotlin.plugin.jdeps
import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.BasicDeclarationChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.CallableChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.ClassLikeChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.FileChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.FunctionChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.expression.QualifiedAccessChecker
import io.bazel.kotlin.plugin.jdeps.k2.checker.expression.ResolvedQualifierChecker
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirCallableDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassLikeChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
internal class JdepsFirExtensions(
private val classUsageRecorder: ClassUsageRecorder,
) : FirExtensionRegistrar() {
override fun ExtensionRegistrarContext.configurePlugin() {
+::JdepsFirCheckersExtension
}
internal inner class JdepsFirCheckersExtension(
session: FirSession,
) : FirAdditionalCheckersExtension(session) {
override val declarationCheckers: DeclarationCheckers =
object : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker> = setOf(BasicDeclarationChecker(classUsageRecorder))
override val fileCheckers: Set<FirFileChecker> = setOf(FileChecker(classUsageRecorder))
override val classLikeCheckers: Set<FirClassLikeChecker> = setOf(ClassLikeChecker(classUsageRecorder))
override val functionCheckers: Set<FirFunctionChecker> = setOf(FunctionChecker(classUsageRecorder))
override val callableDeclarationCheckers: Set<FirCallableDeclarationChecker> = setOf(CallableChecker(classUsageRecorder))
}
override val expressionCheckers: ExpressionCheckers =
object : ExpressionCheckers() {
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker> =
setOf(QualifiedAccessChecker(classUsageRecorder))
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker> =
setOf(ResolvedQualifierChecker(classUsageRecorder))
}
}
}

View File

@@ -0,0 +1,87 @@
package io.bazel.kotlin.plugin.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
@OptIn(org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi::class)
class JdepsGenCommandLineProcessor : CommandLineProcessor {
companion object {
const val COMPILER_PLUGIN_ID = "io.bazel.kotlin.plugin.jdeps.JDepsGen"
val OUTPUT_JDEPS_FILE_OPTION: CliOption = CliOption(
optionName = "output",
valueDescription = "<path>",
description = "Output path for generated jdeps",
required = true,
)
val TARGET_LABEL_OPTION: CliOption = CliOption(
optionName = "target_label",
valueDescription = "<String>",
description = "Label of target being analyzed",
required = true,
)
val DIRECT_DEPENDENCIES_OPTION: CliOption = CliOption(
optionName = "direct_dependencies",
valueDescription = "<List>",
description = "List of targets direct dependencies",
required = false,
allowMultipleOccurrences = true,
)
val STRICT_KOTLIN_DEPS_OPTION: CliOption = CliOption(
optionName = "strict_kotlin_deps",
valueDescription = "<String>",
description = "Report strict deps violations",
required = false,
)
}
override val pluginId: String
get() = COMPILER_PLUGIN_ID
override val pluginOptions: Collection<AbstractCliOption>
get() {
return 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 <T> CompilerConfiguration.appendList(
option: CompilerConfigurationKey<List<T>>,
value: T,
) {
val paths = getList(option).toMutableList()
paths.add(value)
put(option, paths)
}
override fun <T> CompilerConfiguration.appendList(
option: CompilerConfigurationKey<List<T>>,
values: List<T>,
) {
val paths = getList(option).toMutableList()
paths.addAll(values)
put(option, paths)
}
}

View File

@@ -0,0 +1,19 @@
package io.bazel.kotlin.plugin.jdeps
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
@OptIn(org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi::class)
class JdepsGenComponentRegistrar : CompilerPluginRegistrar() {
override val supportsK2: Boolean
get() = true
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
val classUsageRecorder = ClassUsageRecorder()
val genExtension = JdepsGenExtension2(classUsageRecorder, configuration)
FirExtensionRegistrarAdapter.registerExtension(JdepsFirExtensions(classUsageRecorder))
ClassFileFactoryFinalizerExtension.registerExtension(genExtension)
}
}

View File

@@ -0,0 +1,29 @@
package io.bazel.kotlin.plugin.jdeps
import org.jetbrains.kotlin.config.CompilerConfigurationKey
object JdepsGenConfigurationKeys {
/**
* Output path of generated Jdeps proto file.
*/
val OUTPUT_JDEPS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.OUTPUT_JDEPS_FILE_OPTION.description)
/**
* Label of the Bazel target being analyzed.
*/
val TARGET_LABEL: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.TARGET_LABEL_OPTION.description)
/**
* Label of the Bazel target being analyzed.
*/
val STRICT_KOTLIN_DEPS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.STRICT_KOTLIN_DEPS_OPTION.description)
/**
* List of direct dependencies of the target.
*/
val DIRECT_DEPENDENCIES: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create(JdepsGenCommandLineProcessor.DIRECT_DEPENDENCIES_OPTION.description)
}

View File

@@ -0,0 +1,165 @@
package io.bazel.kotlin.plugin.jdeps
import com.google.devtools.build.lib.view.proto.Deps
import org.jetbrains.bazel.jvm.kotlin.JarOwner
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.config.CompilerConfiguration
import java.nio.file.Files
import java.nio.file.Path
internal class JdepsGenExtension2(
private val classUsageRecorder: ClassUsageRecorder,
private val configuration: CompilerConfiguration,
) : ClassFileFactoryFinalizerExtension {
override fun finalizeClassFactory(factory: ClassFileFactory) {
onAnalysisCompleted(
configuration = configuration,
explicitClassesCanonicalPaths = classUsageRecorder.explicitClassesCanonicalPaths,
implicitClassesCanonicalPaths = classUsageRecorder.implicitClassesCanonicalPaths,
)
}
}
private fun onAnalysisCompleted(
explicitClassesCanonicalPaths: Set<String>,
implicitClassesCanonicalPaths: Set<String>,
configuration: CompilerConfiguration,
) {
val directDeps = configuration.getList(JdepsGenConfigurationKeys.DIRECT_DEPENDENCIES)
val targetLabel = configuration.getNotNull(JdepsGenConfigurationKeys.TARGET_LABEL)
val explicitDeps = createDepsMap(explicitClassesCanonicalPaths)
doWriteJdeps(
directDeps = directDeps,
targetLabel = targetLabel,
explicitDeps = explicitDeps,
implicitClassesCanonicalPaths = implicitClassesCanonicalPaths,
configuration = configuration,
)
doStrictDeps(
compilerConfiguration = configuration,
targetLabel = targetLabel,
directDeps = directDeps,
explicitDeps = explicitDeps,
)
}
/**
* Returns a map of jars to classes loaded from those jars.
*/
private fun createDepsMap(classes: Set<String>): Map<String, List<String>> {
val jarsToClasses = HashMap<String, MutableList<String>>()
for (aClass in classes) {
val parts = aClass.split("!/")
val jarPath = parts[0]
if (jarPath.endsWith(".jar")) {
jarsToClasses.computeIfAbsent(jarPath) { ArrayList() }.add(parts[1])
}
}
return jarsToClasses
}
private fun doWriteJdeps(
directDeps: MutableList<String>,
targetLabel: String,
explicitDeps: Map<String, List<String>>,
implicitClassesCanonicalPaths: Set<String>,
configuration: CompilerConfiguration,
) {
val implicitDeps = createDepsMap(implicitClassesCanonicalPaths)
val deps = mutableListOf<Deps.Dependency>()
val unusedDeps = directDeps.subtract(explicitDeps.keys)
for (jarPath in unusedDeps) {
val dependency = Deps.Dependency.newBuilder()
dependency.kind = Deps.Dependency.Kind.UNUSED
dependency.path = jarPath
deps.add(dependency.build())
}
for ((jarPath, _) in explicitDeps) {
val dependency = Deps.Dependency.newBuilder()
dependency.kind = Deps.Dependency.Kind.EXPLICIT
dependency.path = jarPath
deps.add(dependency.build())
}
for (path in implicitDeps.keys.subtract(explicitDeps.keys)) {
val dependency = Deps.Dependency.newBuilder()
dependency.kind = Deps.Dependency.Kind.IMPLICIT
dependency.path = path
deps.add(dependency.build())
}
val rootBuilder = Deps.Dependencies.newBuilder()
rootBuilder.success = true
rootBuilder.ruleLabel = targetLabel
deps.sortBy { it.path }
rootBuilder.addAllDependency(deps)
// build and write out deps.proto
val jdepsOutput = configuration.getNotNull(JdepsGenConfigurationKeys.OUTPUT_JDEPS)
Files.write(Path.of(jdepsOutput), rootBuilder.build().toByteArray())
}
private fun doStrictDeps(
compilerConfiguration: CompilerConfiguration,
targetLabel: String,
directDeps: MutableList<String>,
explicitDeps: Map<String, List<String>>,
) {
when (compilerConfiguration.get(JdepsGenConfigurationKeys.STRICT_KOTLIN_DEPS, "none")) {
"warn" -> checkStrictDeps(explicitDeps, directDeps, targetLabel)
"error" -> {
require(!checkStrictDeps(explicitDeps, directDeps, targetLabel)) {
"Strict Deps Violations - please fix"
}
}
}
}
/**
* Prints strict deps warnings and returns true if violations were found.
*/
private fun checkStrictDeps(
result: Map<String, List<String>>,
directDeps: List<String>,
targetLabel: String,
): Boolean {
val missingStrictDeps = result.keys
.asSequence()
.filter { !directDeps.contains(it) }
.map { JarOwner.readJarOwnerFromManifest(Path.of(it)) }
.toList()
if (missingStrictDeps.isEmpty()) {
return false
}
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
}

View File

@@ -0,0 +1,25 @@
package io.bazel.kotlin.plugin.jdeps
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
internal fun SourceElement.binaryClass(): String? {
return when (this) {
is KotlinJvmBinarySourceElement -> binaryClass.location
is JvmPackagePartSource -> this.knownJvmBinaryClass?.location
is JavaBinarySourceElement -> this.javaClass.virtualFile.path
else -> null
}
}
internal fun DeserializedContainerSource.binaryClass(): String? {
return when (this) {
is JvmPackagePartSource -> this.knownJvmBinaryClass?.location
is KotlinJvmBinarySourceElement -> binaryClass.location
else -> null
}
}

View File

@@ -0,0 +1 @@
io.bazel.kotlin.plugin.jdeps.JdepsGenCommandLineProcessor

View File

@@ -0,0 +1 @@
io.bazel.kotlin.plugin.jdeps.JdepsGenComponentRegistrar

View File

@@ -0,0 +1,32 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
internal class BasicDeclarationChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirBasicDeclarationChecker(MppCheckerKind.Common) {
override fun check(
declaration: FirDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
var visited: HashSet<Pair<ClassId, Boolean>>? = null
for (annotation in declaration.annotations) {
val symbol = annotation.toAnnotationClassLikeSymbol(context.session) ?: continue
if (visited == null) {
visited = HashSet()
}
else {
visited.clear()
}
classUsageRecorder.recordClass(firClass = symbol, context = context, visited = visited)
}
}
}

View File

@@ -0,0 +1,58 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirCallableDeclarationChecker
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.isExtension
import org.jetbrains.kotlin.name.ClassId
internal class CallableChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirCallableDeclarationChecker(MppCheckerKind.Common) {
/**
* Tracks the return type & type parameters of a callable declaration. Function parameters are
* tracked in [FunctionChecker].
*/
override fun check(
declaration: FirCallableDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val visited = HashSet<Pair<ClassId, Boolean>>()
// return type
classUsageRecorder.recordTypeRef(
typeRef = declaration.returnTypeRef,
context = context,
visited = visited,
)
// type params
for (typeParam in declaration.typeParameters) {
for (typeParamBound in typeParam.symbol.resolvedBounds) {
visited.clear()
classUsageRecorder.recordTypeRef(
typeRef = typeParamBound,
context = context,
visited = visited,
)
}
}
// receiver param for extensions
if (declaration !is FirAnonymousFunction) {
declaration.receiverParameter?.typeRef?.let {
visited.clear()
classUsageRecorder.recordTypeRef(
typeRef = it,
context = context,
isExplicit = declaration.isExtension,
visited = visited,
)
}
}
}
}

View File

@@ -0,0 +1,36 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassLikeChecker
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.resolve.getSuperTypes
import org.jetbrains.kotlin.name.ClassId
internal class ClassLikeChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirClassLikeChecker(MppCheckerKind.Common) {
override fun check(
declaration: FirClassLikeDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val visited = HashSet<Pair<ClassId, Boolean>>()
classUsageRecorder.recordClass(
firClass = declaration.symbol,
context = context,
visited = visited,
)
// [recordClass] also handles supertypes, but this marks direct supertypes as explicit
for (path in declaration.symbol.getSuperTypes(useSiteSession = context.session, recursive = false)) {
visited.clear()
classUsageRecorder.recordConeType(
coneKotlinType = path,
context = context,
visited = visited,
)
}
}
}

View File

@@ -0,0 +1,113 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import io.bazel.kotlin.plugin.jdeps.binaryClass
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.name.ClassId
internal class FileChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirFileChecker(MppCheckerKind.Common) {
override fun check(
declaration: FirFile,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val visited = HashSet<Pair<ClassId, Boolean>>()
for (import in declaration.imports.filterIsInstance<FirResolvedImport>()) {
// check for classlike import (class, interface, object, enum, annotation, etc)
if (import.resolvesToClass(context)) {
import.classId()?.resolveToClass(context)?.let {
visited.clear()
classUsageRecorder.recordClass(firClass = it, context = context, visited = visited)
}
} else {
// check for function import
val callableBinaryClass = import.resolveToFun(context)?.containerSource?.binaryClass()
if (callableBinaryClass != null) {
classUsageRecorder.addClass(path = callableBinaryClass, isExplicit = true)
} else {
// for other symbols, track the parent class
import.resolvedParentClassId?.resolveToClass(context)?.let {
visited.clear()
classUsageRecorder.recordClass(firClass = it, context = context, visited = visited)
}
}
}
}
}
}
@Suppress("ReturnCount")
private fun FirResolvedImport.resolveToFun(context: CheckerContext): FirCallableSymbol<*>? {
val funName = this.importedName ?: return null
val topLevelFun = context.session.symbolProvider
.getTopLevelCallableSymbols(packageFqName, funName)
.firstOrNull()
if (topLevelFun != null) {
return topLevelFun
}
val parentClassId = resolvedParentClassId ?: return null
return context.session.getClassDeclaredMemberScope(parentClassId)
?.getFunctions(funName)?.firstOrNull()
}
private fun FirResolvedImport.classId(): ClassId? {
val importedFqName = importedFqName ?: return null
if (importedFqName.isRoot || importedFqName.shortName().asString().isEmpty()) return null
return this.resolvedParentClassId?.createNestedClassId(importedFqName.shortName())
?: ClassId.topLevel(importedFqName)
}
@OptIn(SymbolInternals::class)
private fun FirSession.getClassDeclaredMemberScope(classId: ClassId): FirScope? {
val classSymbol = symbolProvider.getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol ?: return null
return declaredMemberScope(classSymbol.fir, memberRequiredPhase = null)
}
// Below is the original private code from the Kotlin compiler
// https://github.com/JetBrains/kotlin/blob/8b7ca9527a55de33e943f8885b34456030ce9b19/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImportsChecker.kt#L232-L259
@Suppress("UnsafeCallOnNullableType", "UnnecessarySafeCall", "ReturnCount")
private fun FirResolvedImport.resolvesToClass(context: CheckerContext): Boolean {
if (resolvedParentClassId != null) {
if (isAllUnder) return true
val parentClass = resolvedParentClassId!!
val relativeClassName = this.relativeParentClassName ?: return false
val importedName = this.importedName ?: return false
val innerClassId =
ClassId(parentClass.packageFqName, relativeClassName.child(importedName), false)
return innerClassId.resolveToClass(context) != null
} else {
val importedFqName = importedFqName ?: return false
if (importedFqName.isRoot) return false
val importedClassId = ClassId.topLevel(importedFqName)
return importedClassId.resolveToClass(context) != null
}
}
private fun ClassId.resolveToClass(context: CheckerContext): FirRegularClassSymbol? {
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByClassId(this) ?: return null
return when (classSymbol) {
is FirRegularClassSymbol -> classSymbol
is FirTypeAliasSymbol -> classSymbol.fullyExpandedClass(context.session)
is FirAnonymousObjectSymbol -> null
}
}

View File

@@ -0,0 +1,26 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker
import org.jetbrains.kotlin.fir.declarations.FirFunction
internal class FunctionChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirFunctionChecker(MppCheckerKind.Common) {
/**
* Tracks the value parameters of a function declaration. Return type and type parameters are tracked in [CallableChecker].
*/
override fun check(
declaration: FirFunction,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
// function parameters
for (valueParam in declaration.valueParameters) {
valueParam.returnTypeRef.let { classUsageRecorder.recordTypeRef(it, context) }
}
}
}

View File

@@ -0,0 +1,76 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.expression
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import io.bazel.kotlin.plugin.jdeps.binaryClass
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.toResolvedFunctionSymbol
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
import org.jetbrains.kotlin.fir.types.isUnit
import org.jetbrains.kotlin.fir.types.resolvedType
internal class QualifiedAccessChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) {
override fun check(
expression: FirQualifiedAccessExpression,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
// track function's owning class
val resolvedCallableSymbol = expression.toResolvedCallableSymbol()
resolvedCallableSymbol?.containerSource?.binaryClass()?.let {
classUsageRecorder.addClass(path = it, isExplicit = true)
}
// track return type
resolvedCallableSymbol?.resolvedReturnTypeRef?.let {
classUsageRecorder.recordTypeRef(
typeRef = it,
context = context,
isExplicit = false,
collectTypeArguments = false,
)
}
// type arguments
resolvedCallableSymbol?.typeParameterSymbols?.forEach { typeParam ->
typeParam.resolvedBounds.forEach { classUsageRecorder.recordTypeRef(it, context) }
}
// track fun parameter types based on referenced function
expression.calleeReference
.toResolvedFunctionSymbol()
?.valueParameterSymbols
?.forEach { valueParam ->
valueParam.resolvedReturnTypeRef.let {
classUsageRecorder.recordTypeRef(typeRef = it, context = context, isExplicit = false)
}
}
// track fun arguments actually passed
(expression as? FirFunctionCall)?.arguments?.map { it.resolvedType }?.forEach {
classUsageRecorder.recordConeType(
coneKotlinType = it,
context = context,
isExplicit = !it.isExtensionFunctionType,
)
}
// track dispatch receiver
expression.dispatchReceiver?.resolvedType?.let {
if (!it.isUnit) {
classUsageRecorder.recordConeType(
coneKotlinType = it,
context = context,
isExplicit = false,
)
}
}
}
}

View File

@@ -0,0 +1,23 @@
package io.bazel.kotlin.plugin.jdeps.k2.checker.expression
import io.bazel.kotlin.plugin.jdeps.ClassUsageRecorder
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
// Handles expressions such as enum constants and annotation usages
internal class ResolvedQualifierChecker(
private val classUsageRecorder: ClassUsageRecorder,
) : FirResolvedQualifierChecker(MppCheckerKind.Common) {
override fun check(
expression: FirResolvedQualifier,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
expression.symbol?.let {
classUsageRecorder.recordClass(firClass = it, context = context)
}
}
}

View File

@@ -0,0 +1,44 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
load("@rules_graalvm//graalvm:defs.bzl", "native_image")
kt_jvm_library(
name = "worker-lib",
srcs = glob(["*.kt"]),
deps = [
"//src/worker-framework",
"//zip:build-zip",
"//:protobuf-java",
"//:protobuf-java-util",
"@bazel_tools//src/main/protobuf:deps_java_proto",
],
visibility = ["//visibility:public"],
)
java_binary(
name = "worker-jvm",
runtime_deps = [":worker-lib"],
main_class = "org.jetbrains.bazel.jvm.JvmWorker",
jvm_flags = [
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
],
visibility = ["//visibility:public"],
)
native_image(
name = "worker-native",
deps = [":worker-lib"],
extra_args = [
"-H:+UnlockExperimentalVMOptions",
"-H:+CompactingOldGen",
"-Djava.awt.headless=true",
"-Dapple.awt.UIElement=true",
"-march=native",
"-O3",
],
reflection_configuration = ":reflection-config.json",
main_class = "org.jetbrains.bazel.jvm.JvmWorker",
native_image_tool = "@graalvm//:native-image",
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,91 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.bazel.jvm
import com.google.devtools.build.lib.view.proto.Deps
import java.io.*
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarFile
/**
* Persistent worker capable command line program for merging multiple Jdeps files into a single
* file.
*/
private val TARGET_LABEL = Attributes.Name("Target-Label")
private fun readJarOwnerFromManifest(jarPath: Path): String? {
return JarFile(jarPath.toFile()).use { jarFile ->
jarFile.manifest ?: return null
}.mainAttributes.getValue(TARGET_LABEL)
}
internal fun mergeJdeps(
consoleOutput: Writer,
label: String,
inputs: Sequence<Path>,
output: Path,
reportUnusedDeps: String,
): Int {
val rootBuilder = Deps.Dependencies.newBuilder()
rootBuilder.success = false
rootBuilder.ruleLabel = label
val dependencyMap = HashMap<String, Deps.Dependency>()
for (input in inputs) {
val deps = Deps.Dependencies.parseFrom(Files.readAllBytes(input))
for (dep in deps.dependencyList) {
// replace dependency if it has a stronger kind than one we encountered before,
// for example, if new kind is EXPLICIT(0), and old kind is UNUSED(2), we should use dep with EXPLICIT(0)
dependencyMap.merge(dep.path, dep) { old, new -> if (new.kind < old.kind) new else old }
}
}
val dependencies = dependencyMap.values.sortedBy { it.path }
rootBuilder.addAllDependency(dependencies)
rootBuilder.success = true
Files.write(output, rootBuilder.build().toByteArray())
if (reportUnusedDeps == "off") {
return 0
}
val kindMap = LinkedHashMap<String, Deps.Dependency.Kind>()
// a target might produce multiple jars (Android produces `_resources.jar`),
// so we need to make sure we don't mart the dependency as unused unless all the jars are unused
for (dep in dependencies) {
var label = readJarOwnerFromManifest(Path.of(dep.path)) ?: continue
if (label.startsWith("@@") || label.startsWith("@/")) {
label = label.substring(1)
}
if (kindMap.getOrDefault(label, Deps.Dependency.Kind.UNUSED) >= dep.kind) {
kindMap.put(label, dep.kind)
}
}
val unusedLabels = kindMap.entries
.asSequence()
.filter { it.value == Deps.Dependency.Kind.UNUSED }
.map { it.key }
.filter { it != label }
.toList()
if (unusedLabels.isNotEmpty()) {
val open = "\u001b[35m\u001b[1m"
val close = "\u001b[0m"
val message = """
|$open ** Please remove the following dependencies:$close ${
unusedLabels.joinToString(
" ",
)
} from $label
|$open ** You can use the following buildozer command:$close buildozer 'remove deps ${
unusedLabels.joinToString(" ")
}' $label
""".trimMargin()
consoleOutput.append(message)
}
return if (reportUnusedDeps == "error") 1 else 0
}

View File

@@ -5,62 +5,76 @@ import com.google.devtools.build.lib.worker.WorkerProtocol
import org.jetbrains.intellij.build.io.AddDirEntriesMode
import org.jetbrains.intellij.build.io.PackageIndexBuilder
import org.jetbrains.intellij.build.io.W_OVERWRITE
import org.jetbrains.intellij.build.io.ZipFileWriter
import org.jetbrains.intellij.build.io.ZipArchiveOutputStream
import org.jetbrains.intellij.build.io.ZipIndexWriter
import org.jetbrains.intellij.build.io.file
import java.io.File
import java.io.Writer
import java.nio.channels.FileChannel
import java.nio.file.Path
import kotlin.let
import kotlin.system.exitProcess
import kotlin.text.isEmpty
import kotlin.text.substring
private val workingDir = Path.of(".").toAbsolutePath().normalize()
object JvmWorker {
@JvmStatic
fun main(args: Array<String>) {
if (!args.contains("--persistent_worker")) {
System.err.println("Only persistent worker mode is supported")
exitProcess(1)
}
WorkRequestHandler(::handleRequest).processRequests()
fun main(startupArgs: Array<String>) {
WorkRequestHandler(::handleRequest).processRequests(startupArgs)
}
}
private fun handleRequest(workRequest: WorkerProtocol.WorkRequest, output: Writer): Int {
private fun handleRequest(workRequest: WorkerProtocol.WorkRequest, consoleOutput: Writer, baseDir: Path): Int {
val args = workRequest.argumentsList
if (args.isEmpty()) {
output.appendLine("Command is not specified")
consoleOutput.appendLine("Command is not specified")
return 1
}
val command = args.first().split('|', limit = 4)
val command = args.first().split('|', limit = 5)
@Suppress("SpellCheckingInspection")
require(command.size > 2 && command[0] == "--flagfile=") {
"Command format is incorrect: $command"
}
val taskKind = command[1]
if (taskKind != "jar") {
output.appendLine("Command is not supported: $taskKind (command=$command)")
return 1
}
val output = command[2]
var baseDir = workingDir
if (!workRequest.sandboxDir.isNullOrEmpty()) {
baseDir = baseDir.resolve(workRequest.sandboxDir)
}
createZip(
outJar = Path.of(output),
inputs = workRequest.inputsList,
baseDir = baseDir,
stripPrefix = command[3],
)
when (taskKind) {
"jar" -> {
var stripPrefix = command[3]
if (stripPrefix == "" && workRequest.inputsList.isNotEmpty()) {
val p = workRequest.inputsList.first().path
stripPrefix = command[4]
var index = p.indexOf(stripPrefix)
require(index != -1)
stripPrefix = p.substring(0, index + stripPrefix.length)
}
createZip(
outJar = Path.of(output),
inputs = workRequest.inputsList,
baseDir = baseDir,
stripPrefix = stripPrefix,
)
return 0
return 0
}
"jdeps" -> {
val inputs = workRequest.inputsList.asSequence()
.filter { it.path.endsWith(".jdeps") }
.map { baseDir.resolve(it.path) }
//Files.writeString(Path.of("${System.getProperty("user.home")}/f.txt"), inputs.joinToString("\n") { it.toString() })
mergeJdeps(
consoleOutput = consoleOutput,
label = command[3],
output = Path.of(output),
reportUnusedDeps = command[4],
inputs = inputs,
)
return 0
}
else -> {
consoleOutput.appendLine("Command is not supported: $taskKind (command=$command)")
return 1
}
}
}
private fun createZip(outJar: Path, inputs: List<WorkerProtocol.Input>, baseDir: Path, stripPrefix: String) {
@@ -84,17 +98,15 @@ private fun createZip(outJar: Path, inputs: List<WorkerProtocol.Input>, baseDir:
//Files.writeString(Path.of("/tmp/f2.txt"), stripPrefixWithSlash + "\n" + files.joinToString("\n") { it.toString() })
val packageIndexBuilder = PackageIndexBuilder()
// withCrc = false doesn't work correctly yet
ZipFileWriter(
ZipArchiveOutputStream(
channel = FileChannel.open(outJar, W_OVERWRITE),
zipIndexWriter = ZipIndexWriter(indexWriter = packageIndexBuilder.indexWriter),
withCrc = true,
).use { zipFileWriter ->
zipIndexWriter = ZipIndexWriter(packageIndexBuilder.indexWriter)
).use { stream ->
for (path in files) {
val name = path.replace(File.separatorChar, '/')
packageIndexBuilder.addFile(name = name, addClassDir = false)
zipFileWriter.file(nameString = name, file = root.resolve(path))
stream.file(nameString = name, file = root.resolve(path))
}
packageIndexBuilder.writePackageIndex(zipCreator = zipFileWriter, addDirEntriesMode = AddDirEntriesMode.RESOURCE_ONLY)
packageIndexBuilder.writePackageIndex(stream = stream, addDirEntriesMode = AddDirEntriesMode.RESOURCE_ONLY)
}
}

View File

@@ -1,9 +0,0 @@
We do not depend on `"@bazel_worker_java//src/main/java/com/google/devtools/build/lib/worker:work_request_handlers"` as it leads to
```
ERROR: /private/var/tmp/_bazel_develar/c002af20f6ada3e2667e9e2ceaf2ceca/external/rules_jvm_external~~maven~maven/BUILD: no such target '@@rules_jvm_external~~maven~maven//:com_google_protobuf_protobuf_java_util': target 'com_google_protobuf_protobuf_j
```
it looks like it doesn't resolve dependencies of bazel module.
So, we copied `WorkRequestHandler` and `ProtoWorkerMessageProcessor` to our repo as a temporary solution.

View File

@@ -0,0 +1,44 @@
load("@contrib_rules_jvm//docs:stardoc-input.bzl", "java_junit5_test")
load("@protobuf//bazel:java_proto_library.bzl", "java_proto_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
java_proto_library(
name = "worker_protocol_java_proto",
deps = ["@bazel_worker_api//:worker_protocol_proto"],
)
kt_jvm_library(
name = "worker-framework",
srcs = glob(["*.kt"], exclude = ["*Test.kt"]),
deps = [
":worker_protocol_java_proto",
"//:protobuf-java",
"//:protobuf-java-util",
],
visibility = ["//visibility:public"],
)
kt_jvm_library(
name = "worker_test_lib",
srcs = ["WorkRequestHandlerTest.kt"],
associates = [":worker-framework"],
deps = [
":worker_protocol_java_proto",
"@junit_jupiter_api//jar",
"@assertj//jar",
],
runtime_deps = [
"@junit_platform_commons//jar",
"@opentest4j//jar",
"@junit_jupiter_engine//jar",
"@junit_platform_engine//jar",
"@junit_platform_reporting//jar",
"@junit_platform_launcher//jar",
],
)
java_junit5_test(
name = "worker_test",
test_class = "org.jetbrains.bazel.jvm.WorkRequestHandlerTest",
runtime_deps = [":worker_test_lib"],
)

View File

@@ -5,7 +5,7 @@ import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse
import java.io.InputStream
import java.io.OutputStream
internal class ProtoWorkerMessageProcessor(
class ProtoWorkerMessageProcessor(
private val input: InputStream,
private val output: OutputStream
) {

View File

@@ -21,7 +21,6 @@ import java.io.PrintWriter
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest
import java.io.IOException
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse
import io.netty.buffer.ByteBufAllocator
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.StringWriter
@@ -29,21 +28,25 @@ import java.io.Writer
import java.lang.AutoCloseable
import java.lang.Exception
import java.nio.charset.StandardCharsets
import java.nio.file.Path
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import kotlin.system.exitProcess
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
typealias WorkRequestExecutor = (WorkRequest, Writer, Path) -> Int
/**
* A helper class that handles [WorkRequests](https://bazel.build/docs/persistent-workers), including
* [multiplex workers](https://bazel.build/docs/multiplex-worker).
*/
class WorkRequestHandler internal constructor(
class WorkRequestHandler(
/**
* The function to be called after each [WorkRequest] is read.
*/
private val executor: (WorkRequest, Writer) -> Int,
private val executor: WorkRequestExecutor,
/**
* This worker's stderr.
*/
@@ -54,6 +57,8 @@ class WorkRequestHandler internal constructor(
*/
private val cancelHandler: ((Int) -> Unit)? = null,
) {
private val workingDir = Path.of(".").toAbsolutePath().normalize()
/**
* Requests that are currently being processed. Visible for testing.
*/
@@ -71,12 +76,17 @@ class WorkRequestHandler internal constructor(
* or reading from [System.in], which would corrupt the worker protocol.
* When the while loop exits, the original system streams will be swapped back into [System].
*/
fun processRequests() {
fun processRequests(startupArgs: Array<String>) {
if (!startupArgs.contains("--persistent_worker")) {
System.err.println("Only persistent worker mode is supported")
exitProcess(1)
}
// wrap the system streams into a WorkerIO instance to prevent unexpected reads and writes on stdin/stdout
val workerIo = wrapStandardSystemStreams()
try {
// separate thread to be able to interrupt reading `readWorkRequest` (InputStream.read)
startRead(workerIo, finishTimeout = 1.hours).join()
startRead(workerIo = workerIo, finishTimeout = 1.hours).join()
}
finally {
threadPool.shutdownNow()
@@ -223,35 +233,34 @@ class WorkRequestHandler internal constructor(
*/
// visible for tests
internal fun handleRequest(workerIo: WorkerIo, request: WorkRequest, requestState: AtomicReference<Thread>): Boolean {
val baseDir = if (request.sandboxDir.isNullOrEmpty()) workingDir else workingDir.resolve(request.sandboxDir)
var exitCode = 1
val stringWriter = StringWriter()
var isFatalErrorOccurred = false
stringWriter.use { writer ->
try {
exitCode = executor(request, writer)
}
catch (_: InterruptedException) {
// reset interrupted status
Thread.interrupted()
}
catch (e: Throwable) {
PrintWriter(writer).use { e.printStackTrace(it) }
if (e is Error) {
isFatalErrorOccurred = true
}
try {
exitCode = executor(request, stringWriter, baseDir)
}
catch (_: InterruptedException) {
// reset interrupted status
Thread.interrupted()
}
catch (e: Throwable) {
PrintWriter(stringWriter).use { e.printStackTrace(it) }
if (e is Error) {
isFatalErrorOccurred = true
}
}
try {
// read out the captured string for the final WorkResponse output
val captured = workerIo.readCapturedAsUtf8String().trim()
if (!captured.isEmpty()) {
writer.write(captured)
}
}
catch (e: Throwable) {
errorStream.println(e.message)
try {
// read out the captured string for the final WorkResponse output
val captured = workerIo.readCapturedAsUtf8String().trim()
if (!captured.isEmpty()) {
stringWriter.write(captured)
}
}
catch (e: Throwable) {
errorStream.println(e.message)
}
val responseBuilder = WorkResponse.newBuilder()
responseBuilder.setRequestId(request.requestId)
@@ -259,7 +268,11 @@ class WorkRequestHandler internal constructor(
responseBuilder.setWasCancelled(true)
}
else {
responseBuilder.setOutput(responseBuilder.getOutput() + stringWriter).setExitCode(exitCode)
val out = stringWriter.buffer
if (!out.isEmpty()) {
responseBuilder.setOutput(stringWriter.toString())
}
responseBuilder.setExitCode(exitCode)
}
val response = responseBuilder.build()
synchronized(this) {

View File

@@ -58,7 +58,7 @@ class WorkRequestHandlerTest {
fun normalWorkRequest() {
val out = ByteArrayOutputStream()
val handler = WorkRequestHandler(
executor = { args, err -> 1 },
executor = { args, err, _ -> 1 },
errorStream = PrintStream(ByteArrayOutputStream()),
messageProcessor = ProtoWorkerMessageProcessor(input = ByteArrayInputStream(ByteArray(0)), output = out),
)
@@ -76,7 +76,7 @@ class WorkRequestHandlerTest {
fun multiplexWorkRequest() {
val out = ByteArrayOutputStream()
val handler = WorkRequestHandler(
executor = { args, err -> 0 },
executor = { args, err, _ -> 0 },
errorStream = PrintStream(ByteArrayOutputStream()),
messageProcessor = ProtoWorkerMessageProcessor(ByteArray(0).inputStream(), out)
)
@@ -102,7 +102,7 @@ class WorkRequestHandlerTest {
// released when the work request handler thread has noticed the closed stdin and interrupted the work request threads
val workerThreads = ArrayList<WeakReference<Thread>>()
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
// each call to this, runs in its own thread
synchronized(workerThreads) {
workerThreads.add(WeakReference(Thread.currentThread()))
@@ -145,7 +145,7 @@ class WorkRequestHandlerTest {
Thread.sleep(1.seconds.inWholeMilliseconds)
}
throw AssertionError("All worker threads should have stopped: " + workerThreads.joinToString() { it.get()?.toString() ?: "null" })
throw AssertionError("All worker threads should have stopped: " + workerThreads.joinToString { it.get()?.toString() ?: "null" })
}
@Test
@@ -159,7 +159,7 @@ class WorkRequestHandlerTest {
val eternity = Semaphore(0)
val workerThreads = ConcurrentLinkedQueue<WeakReference<Thread>>()
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
// each call to this, runs in its own thread
try {
synchronized(workerThreads) {
@@ -198,7 +198,7 @@ class WorkRequestHandlerTest {
fun testOutput() {
val out = ByteArrayOutputStream()
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
err.appendLine("Failed!")
1
},
@@ -220,7 +220,7 @@ class WorkRequestHandlerTest {
fun testException() {
val out = ByteArrayOutputStream()
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
throw RuntimeException("Exploded!")
},
errorStream = PrintStream(ByteArrayOutputStream()),
@@ -245,7 +245,7 @@ class WorkRequestHandlerTest {
val dest = PipedInputStream()
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
handlerCalled = true
err.appendLine("Such work! Much progress! Wow!")
1
@@ -290,7 +290,7 @@ class WorkRequestHandlerTest {
// we force the regular handling to not finish until after we have read the cancel response, to avoid flakiness
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
// this handler waits until the main thread has sent a cancel request
handlerCalled.release()
try {
@@ -339,7 +339,7 @@ class WorkRequestHandlerTest {
// we force the regular handling to not finish until after we have read the cancel response, to avoid flakiness
val inputStream = PipedInputStream(src)
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
try {
waitForCancel.acquire()
}
@@ -385,7 +385,7 @@ class WorkRequestHandlerTest {
// we force the cancel request to not happen until after we have read the normal response, to avoid flakiness
val handler = WorkRequestHandler(
executor = { args, err ->
executor = { args, err, _ ->
handlerCalled.release()
err.appendLine("Such work! Much progress! Wow!")
2
@@ -418,7 +418,7 @@ class WorkRequestHandlerTest {
fun workRequestHandlerWithWorkRequestCallback() {
val out = ByteArrayOutputStream()
val handler = WorkRequestHandler(
executor = { request, err -> request.argumentsCount },
executor = { request, err, _ -> request.argumentsCount },
errorStream = PrintStream(ByteArrayOutputStream()),
messageProcessor = ProtoWorkerMessageProcessor(ByteArrayInputStream(ByteArray(0)), out),
)

View File

@@ -5,8 +5,6 @@ kt_jvm_library(
module_name = "intellij.idea.community.build.zip",
visibility = ["//visibility:public"],
srcs = glob(["src/*.kt"]),
javac_opts = "//:j17",
kotlinc_opts = "//:k17",
deps = [
"@lib//:kotlin-stdlib",
"@lib//:netty-buffer",

View File

@@ -24,7 +24,11 @@ class PackageIndexBuilder {
}
}
fun writePackageIndex(zipCreator: ZipFileWriter, addDirEntriesMode: AddDirEntriesMode = AddDirEntriesMode.NONE) {
fun writePackageIndex(writer: ZipFileWriter, addDirEntriesMode: AddDirEntriesMode = AddDirEntriesMode.NONE) {
writePackageIndex(stream = writer.resultStream, addDirEntriesMode = addDirEntriesMode)
}
fun writePackageIndex(stream: ZipArchiveOutputStream, addDirEntriesMode: AddDirEntriesMode = AddDirEntriesMode.NONE) {
if (!indexWriter.resourcePackages.isEmpty()) {
// add empty package if top-level directory will be requested
indexWriter.resourcePackages.add(0)
@@ -33,7 +37,6 @@ class PackageIndexBuilder {
val sortedDirsToRegister = dirsToRegister.toTypedArray()
sortedDirsToRegister.sort()
val stream = zipCreator.resultStream
if (addDirEntriesMode == AddDirEntriesMode.NONE) {
for (dirName in sortedDirsToRegister) {
val nameBytes = dirName.encodeToByteArray()

View File

@@ -15,7 +15,7 @@ import java.util.zip.ZipEntry
const val INDEX_FILENAME: String = "__index__"
private val INDEX_FILENAME_BYTES = "__index__".toByteArray()
internal class ZipArchiveOutputStream(
class ZipArchiveOutputStream(
private val channel: GatheringByteChannel,
private val zipIndexWriter: ZipIndexWriter,
) : AutoCloseable {
@@ -167,17 +167,15 @@ internal class ZipArchiveOutputStream(
val headerSize = 30 + name.size
val dataOffset = channelPosition + headerSize
val method = ZipEntry.STORED
buffer.clear()
writeZipLocalFileHeader(name = name, size = size, compressedSize = size, crc32 = 0, method = method, buffer = buffer)
writeZipLocalFileHeader(name = name, size = size, compressedSize = size, crc32 = 0, method = ZipEntry.STORED, buffer = buffer)
assert(buffer.readableBytes() == headerSize)
writeBuffer()
zipIndexWriter.writeCentralFileHeader(
size = size,
compressedSize = size,
method = method,
method = ZipEntry.STORED,
crc = 0,
name = name,
localFileHeaderOffset = localFileHeaderOffset,
@@ -346,12 +344,15 @@ internal class ZipArchiveOutputStream(
return p
}
internal fun transferFrom(fileChannel: FileChannel, size: Long) {
internal fun transferFrom(source: FileChannel, size: Long) {
var position = 0L
val to = this.fileChannel!!
while (position < size) {
position += fileChannel.transferTo(position, size - position, channel)
val n = to.transferFrom(source, channelPosition, size - position)
assert(n >= 0)
position += n
channelPosition += n
}
channelPosition += size
}
private fun writeBuffer(buffer: ByteBuf = this.buffer): Int {

View File

@@ -22,6 +22,8 @@ import java.util.zip.ZipEntry
import kotlin.math.min
val W_CREATE_NEW: EnumSet<StandardOpenOption> = EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
private val WRITE = EnumSet.of(StandardOpenOption.WRITE)
private val READ = EnumSet.of(StandardOpenOption.READ)
// 1 MB
private const val largeFileThreshold = 1_048_576
@@ -29,11 +31,23 @@ private const val compressThreshold = 8 * 1024
// 8 MB (as JDK)
private const val mappedTransferSize = 8L * 1024L * 1024L
@Suppress("DuplicatedCode")
fun ZipArchiveOutputStream.file(nameString: String, file: Path) {
val name = nameString.toByteArray()
FileChannel.open(file, READ).use { channel ->
val size = channel.size()
assert(size <= Int.MAX_VALUE)
writeEntryHeaderWithoutCrc(name = name, size = size.toInt())
transferFrom(channel, size.toLong())
}
return
}
fun transformZipUsingTempFile(file: Path, indexWriter: IkvIndexBuilder?, task: (ZipFileWriter) -> Unit) {
val tempFile = Files.createTempFile(file.parent, file.fileName.toString(), ".tmp")
try {
ZipFileWriter(
channel = FileChannel.open(tempFile, EnumSet.of(StandardOpenOption.WRITE)),
channel = FileChannel.open(tempFile, WRITE),
zipIndexWriter = ZipIndexWriter(indexWriter),
).use {
task(it)
@@ -64,7 +78,6 @@ inline fun writeNewZipWithoutIndex(
class ZipFileWriter(
channel: GatheringByteChannel,
private val deflater: Deflater? = null,
private val withCrc: Boolean = true,
private val zipIndexWriter: ZipIndexWriter,
) : AutoCloseable {
// size is written as part of optimized metadata - so, if compression is enabled, optimized metadata will be incorrect
@@ -82,23 +95,13 @@ class ZipFileWriter(
var isCompressed = deflater != null && !nameString.endsWith(".png")
val name = nameString.toByteArray()
if (!withCrc) {
FileChannel.open(file, EnumSet.of(StandardOpenOption.READ)).use { channel ->
val size = channel.size()
assert(size <= Int.MAX_VALUE)
resultStream.writeEntryHeaderWithoutCrc(name = name, size = size.toInt())
resultStream.transferFrom(fileChannel = channel, size.toLong())
}
return
}
crc32.reset()
val headerSize = 30 + name.size
var input: ByteBuf? = null
try {
val size: Int
FileChannel.open(file, EnumSet.of(StandardOpenOption.READ)).use { channel ->
FileChannel.open(file, READ).use { channel ->
size = channel.size().toInt()
if (size == 0) {
resultStream.writeEmptyFile(name = name)

View File

@@ -100,7 +100,7 @@ fun zip(
},
dirs = dirs,
)
packageIndexBuilder.writePackageIndex(zipCreator = zipFileWriter, addDirEntriesMode = addDirEntriesMode)
packageIndexBuilder.writePackageIndex(writer = zipFileWriter, addDirEntriesMode = addDirEntriesMode)
}
}
}

View File

@@ -1,14 +1,11 @@
### auto-generated section `build intellij.idea.tools.launch` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_test")
jvm_library(
name = "idea-tools-launch",
module_name = "intellij.idea.tools.launch",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",
@@ -21,12 +18,10 @@ jvm_library(
]
)
kt_jvm_test(
name = "idea-tools-launch_test",
jvm_library(
name = "idea-tools-launch_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True),
associates = [":idea-tools-launch"],
deps = [
"@lib//:kotlin-stdlib",
@@ -38,8 +33,13 @@ kt_jvm_test(
"//platform/build-scripts/downloader:buildScripts-downloader",
"@lib//:junit5",
"//platform/testFramework/junit5",
"//platform/testFramework/junit5:junit5_test",
"//platform/testFramework/junit5:junit5_test_lib",
"//platform/ijent/buildConstants:community-buildConstants",
]
)
jvm_test(
name = "idea-tools-launch_test",
runtime_deps = [":idea-tools-launch_test_lib"]
)
### auto-generated section `build intellij.idea.tools.launch` end

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,5 @@
### auto-generated section `build intellij.idea.community.build.tasks` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources", "jvm_test")
jvm_resources(
name = "tasks_resources",
@@ -13,10 +11,7 @@ jvm_library(
name = "tasks",
module_name = "intellij.idea.community.build.tasks",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
plugins = ["@lib//:serialization_plugin"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-serialization-core",
@@ -50,14 +45,11 @@ jvm_library(
]
)
kt_jvm_test(
name = "tasks_test",
jvm_library(
name = "tasks_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True),
associates = [":tasks"],
plugins = ["@lib//:serialization_plugin"],
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-serialization-core",
@@ -73,10 +65,12 @@ kt_jvm_test(
"//platform/util/rt-java8",
"//java/java-runtime:rt",
"//platform/util-rt",
"//platform/util-rt:util-rt_test_lib",
"//platform/diagnostic/telemetry",
"//platform/diagnostic/telemetry:telemetry_test_lib",
"//platform/diagnostic/telemetry.exporters:telemetry-exporters",
"//platform/testFramework",
"//platform/testFramework:testFramework_test",
"//platform/testFramework:testFramework_test_lib",
"//platform/testFramework/extensions:testExtensions",
"@lib//:assert_j",
"@lib//:memoryfilesystem",
@@ -91,4 +85,9 @@ kt_jvm_test(
],
runtime_deps = [":tasks_resources"]
)
jvm_test(
name = "tasks_test",
runtime_deps = [":tasks_test_lib"]
)
### auto-generated section `build intellij.idea.community.build.tasks` end

View File

@@ -1,18 +1,16 @@
### auto-generated section `build intellij.idea.community.build.tests` start
load("@rules_java//java:defs.bzl", "java_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_test")
java_library(
name = "tests",
visibility = ["//visibility:public"]
)
kt_jvm_test(
name = "tests_test",
jvm_library(
name = "tests_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["testSrc/**/*.kt", "testSrc/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["testSrc/**/*.kt", "testSrc/**/*.java"], allow_empty = True),
deps = [
"//build",
"//platform/build-scripts/testFramework:buildScripts-testFramework",
@@ -24,4 +22,9 @@ kt_jvm_test(
"//platform/build-scripts/downloader:buildScripts-downloader",
]
)
jvm_test(
name = "tests_test",
runtime_deps = [":tests_test_lib"]
)
### auto-generated section `build intellij.idea.community.build.tests` end

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.commandInterface` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "commandInterface_resources",
@@ -12,9 +11,7 @@ jvm_library(
name = "commandInterface",
module_name = "intellij.commandInterface",
visibility = ["//visibility:public"],
srcs = glob(["gen/**/*.kt", "gen/**/*.java", "src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["gen/**/*.kt", "gen/**/*.java", "src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//platform/core-impl",
"//platform/analysis-api:analysis",

View File

@@ -1,6 +1,6 @@
### auto-generated section `build fleet.kernel` start
load("@rules_jvm//:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("//build:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "jvm_library")
create_kotlinc_options(
name = "custom",
@@ -16,28 +16,24 @@ jvm_library(
module_name = "fleet.kernel",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = ":custom",
plugins = ["@lib//:serialization_plugin"],
deps = [
"@lib//:kotlin-stdlib",
"//fleet/rhizomedb",
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
"@lib//:rhizomedb-compiler-plugin.provided",
"@lib//:rhizomedb-compiler-plugin-provided",
"//fleet/util/core:fleet-util-core",
"//fleet/rpc",
"@lib//:kotlinx-coroutines-core",
"//fleet/preferences",
"//fleet/reporting/api:fleet-reporting-api",
"@lib//:fastutil-min",
],
exports = [
"//fleet/rhizomedb",
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
"//fleet/util/core:fleet-util-core",
"//fleet/rpc",
"@lib//:kotlinx-coroutines-core",
]
)
### auto-generated section `build fleet.kernel` end

View File

@@ -1,16 +1,15 @@
### auto-generated section `build fleet.preferences` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "preferences",
module_name = "fleet.preferences",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
deps = [
"@lib//:kotlin-stdlib",
"//fleet/util/os:fleet-util-os",
"@lib//:expects-compiler-plugin-provided",
]
)
### auto-generated section `build fleet.preferences` end

View File

@@ -1,13 +1,11 @@
### auto-generated section `build fleet.reporting.api` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "fleet-reporting-api",
module_name = "fleet.reporting.api",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",

View File

@@ -1,6 +1,6 @@
### auto-generated section `build fleet.rhizomedb` start
load("@rules_jvm//:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("//build:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "jvm_library")
create_kotlinc_options(
name = "custom",
@@ -14,7 +14,6 @@ jvm_library(
module_name = "fleet.rhizomedb",
visibility = ["//visibility:public"],
srcs = glob(["src/main/kotlin/**/*.kt", "src/main/kotlin/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = ":custom",
deps = [
"//fleet/util/core:fleet-util-core",
@@ -22,6 +21,8 @@ jvm_library(
"@lib//:fastutil-min",
"@lib//:kotlinx-collections-immutable",
"@lib//:jetbrains-annotations",
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
]
)
### auto-generated section `build fleet.rhizomedb` end

View File

@@ -1,14 +1,11 @@
### auto-generated section `build fleet.rpc.server` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "rpc-server",
module_name = "fleet.rpc.server",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
plugins = ["@lib//:serialization_plugin"],
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",

View File

@@ -1,6 +1,6 @@
### auto-generated section `build fleet.rpc` start
load("@rules_jvm//:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("//build:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "jvm_library")
create_kotlinc_options(
name = "custom",
@@ -16,9 +16,7 @@ jvm_library(
module_name = "fleet.rpc",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = ":custom",
plugins = ["@lib//:serialization_plugin"],
deps = [
"//fleet/reporting/api:fleet-reporting-api",
"//fleet/preferences",
@@ -29,7 +27,7 @@ jvm_library(
"@lib//:jetbrains-annotations",
"@lib//:kotlinx-coroutines-slf4j",
"@lib//:kotlinx-coroutines-core",
"@lib//:rpc-compiler-plugin.provided",
"@lib//:rpc-compiler-plugin-provided",
"//fleet/util/core:fleet-util-core",
"@lib//:opentelemetry",
"@lib//:opentelemetry-semconv",

View File

@@ -1,14 +1,11 @@
### auto-generated section `build fleet.util.core` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "fleet-util-core",
module_name = "fleet.util.core",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
plugins = ["@lib//:serialization_plugin"],
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",
@@ -24,10 +21,7 @@ jvm_library(
"@lib//:slf4j-api",
],
exports = [
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
"//fleet/util/logging/api:fleet-util-logging-api",
"@lib//:fastutil-min",
"@lib//:kotlinx-collections-immutable",
]
)

View File

@@ -1,13 +1,11 @@
### auto-generated section `build fleet.util.logging.api` start
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "fleet-util-logging-api",
module_name = "fleet.util.logging.api",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
deps = [
"@lib//:kotlin-stdlib",
"@lib//:slf4j-api",

View File

@@ -0,0 +1,11 @@
### auto-generated section `build fleet.util.multiplatform` start
load("@rules_jvm//:jvm.bzl", "jvm_library")
jvm_library(
name = "fleet-util-multiplatform",
module_name = "fleet.util.multiplatform",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java", "srcJvmMain/**/*.kt", "srcJvmMain/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
deps = ["@lib//:kotlin-stdlib"]
)
### auto-generated section `build fleet.util.multiplatform` end

View File

@@ -1,6 +1,6 @@
### auto-generated section `build fleet.util.os` start
load("@rules_jvm//:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("//build:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "jvm_library")
create_kotlinc_options(
name = "custom",
@@ -13,7 +13,6 @@ jvm_library(
module_name = "fleet.util.os",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = ":custom"
)
### auto-generated section `build fleet.util.os` end

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.idea.customization.base` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "idea-customization-base_resources",
@@ -12,9 +11,7 @@ jvm_library(
name = "idea-customization-base",
module_name = "intellij.idea.customization.base",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//platform/platform-impl:ide-impl",
"//platform/platform-util-io:ide-util-io",

View File

@@ -1,7 +1,5 @@
### auto-generated section `build intellij.platform.images` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources", "jvm_test")
jvm_resources(
name = "images_resources",
@@ -13,10 +11,7 @@ jvm_library(
name = "images",
module_name = "intellij.platform.images",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
plugins = ["@lib//:serialization_plugin"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//platform/lang-api:lang",
"//platform/util:util-ui",
@@ -36,22 +31,20 @@ jvm_library(
runtime_deps = [":images_resources"]
)
kt_jvm_test(
name = "images_test",
jvm_library(
name = "images_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True),
associates = [":images"],
plugins = ["@lib//:serialization_plugin"],
deps = [
"//platform/lang-api:lang",
"//platform/util:util-ui",
"//platform/lang-impl",
"//platform/platform-impl:ide-impl",
"//platform/platform-impl:ide-impl_test_lib",
"@lib//:commons-imaging",
"//platform/testFramework",
"//platform/testFramework:testFramework_test",
"//platform/testFramework:testFramework_test_lib",
"//platform/core-ui",
"//platform/platform-util-io:ide-util-io",
"//platform/util/jdom",
@@ -64,4 +57,9 @@ kt_jvm_test(
],
runtime_deps = [":images_resources"]
)
jvm_test(
name = "images_test",
runtime_deps = [":images_test_lib"]
)
### auto-generated section `build intellij.platform.images` end

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.platform.images.backend.svg` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "backend-svg_resources",
@@ -12,9 +11,7 @@ jvm_library(
name = "backend-svg",
module_name = "intellij.platform.images.backend.svg",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//images",
"@lib//:kotlin-stdlib",

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.platform.images.copyright` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "copyright_resources",
@@ -12,9 +11,7 @@ jvm_library(
name = "copyright",
module_name = "intellij.platform.images.copyright",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//images",
"//plugins/copyright",

View File

@@ -1,20 +1,18 @@
### auto-generated section `build intellij.java.compiler.tests` start
load("@rules_java//java:defs.bzl", "java_library")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_test")
java_library(
name = "compiler-tests",
visibility = ["//visibility:public"]
)
kt_jvm_test(
name = "compiler-tests_test",
jvm_library(
name = "compiler-tests_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["tests/**/*.kt", "tests/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["tests/**/*.kt", "tests/**/*.java"], allow_empty = True),
associates = ["//java/compiler/impl:java-compiler-impl"],
deps = [
"//java/compiler/impl:java-compiler-impl",
"//java/compiler/openapi:java-compiler",
"//java/testFramework",
"//java/openapi:java",
@@ -29,6 +27,12 @@ kt_jvm_test(
"//platform/util/jdom",
"@lib//:kotlinx-coroutines-core",
"//platform/backend/workspace",
]
],
runtime_deps = ["//spellchecker"]
)
jvm_test(
name = "compiler-tests_test",
runtime_deps = [":compiler-tests_test_lib"]
)
### auto-generated section `build intellij.java.compiler.tests` end

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.java.compiler.charts` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "java-compiler-charts_resources",
@@ -12,13 +11,12 @@ jvm_library(
name = "java-compiler-charts",
module_name = "intellij.java.compiler.charts",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j17",
kotlinc_opts = "@rules_jvm//:k17",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"//java/compiler/impl:java-compiler-impl",
"//jps/jps-builders:build",
"//platform/extensions",
"//platform/ide-core-impl",
"//platform/core-api:core",
"//platform/lang-api:lang",
"//platform/lang-impl",

View File

@@ -1,6 +1,5 @@
### auto-generated section `build intellij.java.compiler.charts.jps` start
load("@rules_jvm//:jvm.bzl", "jvm_resources")
load("@rules_jvm//:rules.bzl", "jvm_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "java-compiler-charts-jps_resources",
@@ -12,9 +11,9 @@ jvm_library(
name = "java-compiler-charts-jps",
module_name = "intellij.java.compiler.charts.jps",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True, exclude = ["**/module-info.java"]),
javac_opts = "@rules_jvm//:j11",
kotlinc_opts = "@rules_jvm//:k11",
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
javac_opts = "@community//:j11",
kotlinc_opts = "@community//:k11",
deps = [
"//jps/jps-builders:build",
"@lib//:gson",

Some files were not shown because too many files have changed in this diff Show More