mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
do not use _test as lib - test targets do not support this
GitOrigin-RevId: f2f49db4294b6a64040dbbd5f1d95e972cbd70a3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ee348c2a40
commit
98799639aa
3
.bazelrc
3
.bazelrc
@@ -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
|
||||
|
||||
|
||||
30
BUILD.bazel
30
BUILD.bazel
@@ -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
|
||||
|
||||
10
MODULE.bazel
10
MODULE.bazel
@@ -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",
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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"]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
)
|
||||
|
||||
@@ -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=",
|
||||
)
|
||||
|
||||
4388
build/jvm-rules/MODULE.bazel.lock
generated
4388
build/jvm-rules/MODULE.bazel.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
136
build/jvm-rules/rules/common-attrs.bzl
Normal file
136
build/jvm-rules/rules/common-attrs.bzl
Normal 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",
|
||||
)
|
||||
21
build/jvm-rules/rules/impl/builder-args.bzl
Normal file
21
build/jvm-rules/rules/impl/builder-args.bzl
Normal 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
|
||||
797
build/jvm-rules/rules/impl/compile.bzl
Normal file
797
build/jvm-rules/rules/impl/compile.bzl
Normal 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
|
||||
382
build/jvm-rules/rules/impl/kotlinc-options.bzl
Normal file
382
build/jvm-rules/rules/impl/kotlinc-options.bzl
Normal 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)
|
||||
33
build/jvm-rules/rules/import.bzl
Normal file
33
build/jvm-rules/rules/import.bzl
Normal 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,
|
||||
)
|
||||
89
build/jvm-rules/rules/library.bzl
Normal file
89
build/jvm-rules/rules/library.bzl
Normal 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],
|
||||
)
|
||||
26
build/jvm-rules/rules/provided-library.bzl
Normal file
26
build/jvm-rules/rules/provided-library.bzl
Normal 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,
|
||||
)
|
||||
70
build/jvm-rules/rules/resource.bzl
Normal file
70
build/jvm-rules/rules/resource.bzl
Normal 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],
|
||||
)
|
||||
178
build/jvm-rules/rules/test.bzl
Normal file
178
build/jvm-rules/rules/test.bzl
Normal 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
|
||||
)
|
||||
10
build/jvm-rules/src/jar/BUILD.bazel
Normal file
10
build/jvm-rules/src/jar/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
32
build/jvm-rules/src/jar/JarOwner.kt
Normal file
32
build/jvm-rules/src/jar/JarOwner.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
build/jvm-rules/src/kotlin-builder/ArgMap.kt
Normal file
87
build/jvm-rules/src/kotlin-builder/ArgMap.kt
Normal 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)
|
||||
}
|
||||
49
build/jvm-rules/src/kotlin-builder/BUILD.bazel
Normal file
49
build/jvm-rules/src/kotlin-builder/BUILD.bazel
Normal 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",
|
||||
)
|
||||
102
build/jvm-rules/src/kotlin-builder/CompilationArgs.kt
Normal file
102
build/jvm-rules/src/kotlin-builder/CompilationArgs.kt
Normal 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()
|
||||
}
|
||||
109
build/jvm-rules/src/kotlin-builder/CompilationTaskContext.kt
Normal file
109
build/jvm-rules/src/kotlin-builder/CompilationTaskContext.kt
Normal 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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
build/jvm-rules/src/kotlin-builder/JarCreator.kt
Normal file
141
build/jvm-rules/src/kotlin-builder/JarCreator.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
30
build/jvm-rules/src/kotlin-builder/KotlinBuildWorker.kt
Normal file
30
build/jvm-rules/src/kotlin-builder/KotlinBuildWorker.kt
Normal 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)
|
||||
}
|
||||
200
build/jvm-rules/src/kotlin-builder/KotlinBuilder.kt
Normal file
200
build/jvm-rules/src/kotlin-builder/KotlinBuilder.kt
Normal 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
|
||||
}
|
||||
|
||||
43
build/jvm-rules/src/kotlin-builder/KotlinBuilderFlags.kt
Normal file
43
build/jvm-rules/src/kotlin-builder/KotlinBuilderFlags.kt
Normal 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,
|
||||
}
|
||||
273
build/jvm-rules/src/kotlin-builder/KotlinJvmTaskExecutor.kt
Normal file
273
build/jvm-rules/src/kotlin-builder/KotlinJvmTaskExecutor.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
31
build/jvm-rules/src/kotlin-builder/KotlinToolException.kt
Normal file
31
build/jvm-rules/src/kotlin-builder/KotlinToolException.kt
Normal 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)
|
||||
55
build/jvm-rules/src/kotlin-builder/task.kt
Normal file
55
build/jvm-rules/src/kotlin-builder/task.kt
Normal 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>,
|
||||
)
|
||||
|
||||
19
build/jvm-rules/src/kotlin-plugins/abi/BUILD.bazel
Normal file
19
build/jvm-rules/src/kotlin-plugins/abi/BUILD.bazel
Normal 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__"],
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
269
build/jvm-rules/src/kotlin-plugins/abi/JvmAbiOutputExtension.kt
Normal file
269
build/jvm-rules/src/kotlin-plugins/abi/JvmAbiOutputExtension.kt
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
21
build/jvm-rules/src/kotlin-plugins/jdeps/BUILD.bazel
Normal file
21
build/jvm-rules/src/kotlin-plugins/jdeps/BUILD.bazel
Normal 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__"],
|
||||
)
|
||||
140
build/jvm-rules/src/kotlin-plugins/jdeps/ClassUsageRecorder.kt
Normal file
140
build/jvm-rules/src/kotlin-plugins/jdeps/ClassUsageRecorder.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
165
build/jvm-rules/src/kotlin-plugins/jdeps/JdepsGenExtension2.kt
Normal file
165
build/jvm-rules/src/kotlin-plugins/jdeps/JdepsGenExtension2.kt
Normal 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
|
||||
}
|
||||
25
build/jvm-rules/src/kotlin-plugins/jdeps/JdepsK2Utils.kt
Normal file
25
build/jvm-rules/src/kotlin-plugins/jdeps/JdepsK2Utils.kt
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
io.bazel.kotlin.plugin.jdeps.JdepsGenCommandLineProcessor
|
||||
@@ -0,0 +1 @@
|
||||
io.bazel.kotlin.plugin.jdeps.JdepsGenComponentRegistrar
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
44
build/jvm-rules/src/misc/BUILD.bazel
Normal file
44
build/jvm-rules/src/misc/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
91
build/jvm-rules/src/misc/JdepsMerger.kt
Normal file
91
build/jvm-rules/src/misc/JdepsMerger.kt
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
44
build/jvm-rules/src/worker-framework/BUILD.bazel
Normal file
44
build/jvm-rules/src/worker-framework/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
@@ -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
|
||||
) {
|
||||
@@ -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) {
|
||||
@@ -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),
|
||||
)
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -100,7 +100,7 @@ fun zip(
|
||||
},
|
||||
dirs = dirs,
|
||||
)
|
||||
packageIndexBuilder.writePackageIndex(zipCreator = zipFileWriter, addDirEntriesMode = addDirEntriesMode)
|
||||
packageIndexBuilder.writePackageIndex(writer = zipFileWriter, addDirEntriesMode = addDirEntriesMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
|
||||
11
fleet/util/multiplatform/BUILD.bazel
Normal file
11
fleet/util/multiplatform/BUILD.bazel
Normal 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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user