Skip to content

Commit 5f6e0f2

Browse files
committed
Create a new "bootstrap" toolchain.
Given that Kotlin 2.1.0 now has a version metadata check during compilation, the idea of bootstrapping kotlin by using the previous is pretty much dead. Probably not the worst thing, as bzlmod did not enjoy the shennanigans necessary to make it work. This adds a new "cli" toolchain that invokes kotlinc via the established command line interface. Additionally, it sketches out a simpler, more contained toolchain api to build the more complex usecases (such as multiplex workers, jdeps, compiler plugins, etc.) on top of. the end goal is a more agile rule approach -- with the performance optimization (jdeps, class path reductions, etc) tucked behindthe simple api.Compiler plugins, linting, and the like should be able to live comfortably in the "production" rule and toolchain combinations. It also introduces new "core" rules, while limited in scope, should be IDE friendly and generally easier to work with than the previous bootstrapping effort -- and since they use the common providers, they will interoperate with the existing rules.
1 parent f054b77 commit 5f6e0f2

File tree

16 files changed

+766
-22
lines changed

16 files changed

+766
-22
lines changed

MODULE.bazel

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ use_repo(
3636
"released_com_github_jetbrains_kotlin",
3737
)
3838

39-
register_toolchains("@released_rules_kotlin//kotlin/internal:default_toolchain")
39+
# Register bootstrap toolchain
40+
register_toolchains("//src/main/kotlin:bootstrap_toolchain")
41+
register_toolchains("//src/main/starlark/core/compile/...")
4042

4143
# Back to the regularly scheduled configuration.
4244
register_toolchains("//kotlin/internal:default_toolchain")

kotlin/internal/defs.bzl

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.#
14+
load(
15+
"//src/main/starlark/core/compile:common.bzl",
16+
_JAVA_RUNTIME_TOOLCHAIN_TYPE = "JAVA_RUNTIME_TOOLCHAIN_TYPE",
17+
_JAVA_TOOLCHAIN_TYPE = "JAVA_TOOLCHAIN_TYPE",
18+
_KtJvmInfo = "KtJvmInfo",
19+
)
1420
load(
1521
"//src/main/starlark/core/plugin:providers.bzl",
1622
_KspPluginInfo = "KspPluginInfo",
@@ -23,30 +29,16 @@ load(
2329
TOOLCHAIN_TYPE = "%s" % Label("//kotlin/internal:kt_toolchain_type")
2430

2531
# Java toolchains
26-
JAVA_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:toolchain_type"
27-
JAVA_RUNTIME_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:runtime_toolchain_type"
32+
JAVA_TOOLCHAIN_TYPE = _JAVA_TOOLCHAIN_TYPE
33+
JAVA_RUNTIME_TOOLCHAIN_TYPE = _JAVA_RUNTIME_TOOLCHAIN_TYPE
2834

2935
# The name of the Kotlin compiler workspace.
3036
KT_COMPILER_REPO = "com_github_jetbrains_kotlin"
3137

3238
# The name of the KSP compiler plugin workspace
3339
KSP_COMPILER_PLUGIN_REPO = "com_github_google_ksp"
3440

35-
KtJvmInfo = provider(
36-
fields = {
37-
"module_name": "the module name",
38-
"module_jars": "Jars comprising the module (logical compilation unit), a.k.a. associates",
39-
"exported_compiler_plugins": "compiler plugins to be invoked by targets depending on this.",
40-
"srcs": "the source files. [intelij-aspect]",
41-
"outputs": "output jars produced by this rule. [intelij-aspect]",
42-
"language_version": "version of kotlin used. [intellij-aspect]",
43-
"transitive_compile_time_jars": "Returns the transitive set of Jars required to build the target. [intellij-aspect]",
44-
"transitive_source_jars": "Returns the Jars containing source files of the current target and all of its transitive dependencies. [intellij-aspect]",
45-
"annotation_processing": "Generated annotation processing jars. [intellij-aspect]",
46-
"additional_generated_source_jars": "Returns additional Jars containing generated source files from kapt, ksp, etc. [bazel-bsp-aspect]",
47-
"all_output_jars": "Returns all the output Jars produced by this rule. [bazel-bsp-aspect]",
48-
},
49-
)
41+
KtJvmInfo = _KtJvmInfo
5042

5143
KtCompilerPluginInfo = _KtCompilerPluginInfo
5244

src/main/kotlin/BUILD

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("@released_rules_kotlin//kotlin:core.bzl", "define_kt_toolchain")
12
load("//src/main/starlark/release:packager.bzl", "release_archive")
23
load("//third_party:jarjar.bzl", "jar_jar")
34

@@ -48,3 +49,10 @@ release_archive(
4849
"//src/main/kotlin/io/bazel/kotlin/compiler:pkg",
4950
],
5051
)
52+
53+
define_kt_toolchain(
54+
name = "bootstrap_toolchain",
55+
api_version = "2.0",
56+
jvm_target = "11",
57+
language_version = "2.0",
58+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
toolchain_type(
2+
name = "toolchain_type",
3+
visibility = [
4+
"//visibility:public",
5+
],
6+
)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
load("//src/main/starlark/core/compile:common.bzl", "TYPE")
2+
load("//src/main/starlark/core/repositories:versions.bzl", "versions")
3+
load(":toolchain.bzl", "cli_toolchain")
4+
5+
java_import(
6+
name = "kotlinc_jar",
7+
jars = [
8+
"@com_github_jetbrains_kotlin//:kotlin-compiler",
9+
],
10+
deps = [],
11+
)
12+
13+
java_binary(
14+
name = "kotlinc",
15+
main_class = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler",
16+
runtime_deps = [":kotlinc_jar"],
17+
)
18+
19+
cli_toolchain(
20+
name = "cli_toolchain",
21+
api_version = versions.KOTLIN_CURRENT_COMPILER_RELEASE.version,
22+
kotlin_stdlibs = [
23+
"//kotlin/compiler:annotations",
24+
"//kotlin/compiler:kotlin-stdlib",
25+
"//kotlin/compiler:kotlin-stdlib-jdk7",
26+
"//kotlin/compiler:kotlin-stdlib-jdk8",
27+
],
28+
kotlinc = ":kotlinc",
29+
language_version = versions.KOTLIN_CURRENT_COMPILER_RELEASE.version,
30+
zip = "@bazel_tools//tools/zip:zipper",
31+
)
32+
33+
toolchain(
34+
name = "cli",
35+
toolchain = ":cli_toolchain",
36+
toolchain_type = TYPE,
37+
)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
load("//src/main/starlark/core/compile:common.bzl", "TYPE")
2+
3+
def compile_kotlin_for_jvm(
4+
actions,
5+
srcs,
6+
dep_jars,
7+
class_jar,
8+
module_name,
9+
path_separator,
10+
toolchain_info,
11+
kotlinc_opts,
12+
output_srcjar = None):
13+
if not srcs:
14+
actions.run(
15+
outputs = [output_srcjar],
16+
executable = toolchain_info.executable_zip,
17+
inputs = srcs,
18+
arguments = [actions.args().add("c").add(output_srcjar)],
19+
mnemonic = "EmptyJar",
20+
toolchain = TYPE,
21+
)
22+
actions.run(
23+
outputs = [class_jar],
24+
executable = toolchain_info.executable_zip,
25+
inputs = srcs,
26+
arguments = [actions.args().add("c").add(output_srcjar)],
27+
mnemonic = "EmptyJar",
28+
toolchain = TYPE,
29+
)
30+
return
31+
32+
classpath = depset(
33+
transitive = [
34+
dep_jars,
35+
toolchain_info.kotlin_stdlib.transitive_compile_time_jars,
36+
],
37+
)
38+
39+
args = actions.args().add("-d", class_jar)
40+
args.add("-jdk-home", toolchain_info.java_runtime.java_home)
41+
args.add("-jvm-target", toolchain_info.jvm_target)
42+
args.add("-no-stdlib")
43+
args.add("-verbose")
44+
args.add("-api-version", toolchain_info.api_version)
45+
args.add("-language-version", toolchain_info.language_version)
46+
args.add("-module-name", module_name)
47+
args.add_joined("-cp", classpath, join_with = path_separator)
48+
for (k, v) in kotlinc_opts.items():
49+
args.add(k, v)
50+
args.add_all(srcs)
51+
52+
actions.run(
53+
outputs = [class_jar],
54+
executable = toolchain_info.kotlinc,
55+
inputs = depset(direct = srcs, transitive = [classpath, toolchain_info.java_runtime.files]),
56+
arguments = [args],
57+
mnemonic = toolchain_info.compile_mnemonic,
58+
toolchain = TYPE,
59+
)
60+
61+
if output_srcjar:
62+
actions.run(
63+
outputs = [output_srcjar],
64+
executable = toolchain_info.executable_zip,
65+
inputs = srcs,
66+
arguments = [actions.args().add("c").add(output_srcjar).add_all(srcs)],
67+
mnemonic = "SourceJar",
68+
toolchain = TYPE,
69+
)
70+
71+
def write_jvm_launcher(toolchain_info, actions, path_separator, workspace_prefix, jvm_flags, runtime_jars, main_class, executable_output):
72+
template = toolchain_info.java_stub_template
73+
java_runtime = toolchain_info.java_runtime
74+
java_bin_path = java_runtime.java_executable_runfiles_path
75+
76+
# Following https://github.com/bazelbuild/bazel/blob/6d5b084025a26f2f6d5041f7a9e8d302c590bc80/src/main/starlark/builtins_bzl/bazel/java/bazel_java_binary.bzl#L66-L67
77+
# Enable the security manager past deprecation.
78+
# On bazel 6, this check isn't possible...
79+
if getattr(java_runtime, "version", 0) >= 17:
80+
jvm_flags = jvm_flags + " -Djava.security.manager=allow"
81+
82+
classpath = path_separator.join(
83+
["${RUNPATH}%s" % (j.short_path) for j in runtime_jars.to_list() + toolchain_info.kotlin_stdlib.transitive_compile_time_jars.to_list()],
84+
)
85+
needs_runfiles = "0" if java_bin_path.startswith("/") or (len(java_bin_path) > 2 and java_bin_path[1] == ":") else "1"
86+
87+
actions.expand_template(
88+
template = template,
89+
output = executable_output,
90+
substitutions = {
91+
"%classpath%": classpath,
92+
"%runfiles_manifest_only%": "",
93+
"%java_start_class%": main_class,
94+
"%javabin%": "JAVABIN=" + java_bin_path,
95+
"%jvm_flags%": jvm_flags,
96+
"%set_jacoco_metadata%": "",
97+
"%set_jacoco_main_class%": "",
98+
"%set_jacoco_java_runfiles_root%": "",
99+
"%set_java_coverage_new_implementation%": """export JAVA_COVERAGE_NEW_IMPLEMENTATION=NO""",
100+
"%workspace_prefix%": workspace_prefix,
101+
"%test_runtime_classpath_file%": "export TEST_RUNTIME_CLASSPATH_FILE=${JAVA_RUNFILES}",
102+
"%needs_runfiles%": needs_runfiles,
103+
},
104+
is_executable = True,
105+
)
106+
107+
return depset(
108+
transitive = [
109+
runtime_jars,
110+
java_runtime.files,
111+
toolchain_info.kotlin_stdlib.transitive_compile_time_jars,
112+
],
113+
)
114+
115+
def build_deploy_jar(toolchain_info, actions, jars, output_jar):
116+
args = actions.args()
117+
args.add("--exclude_build_data")
118+
args.add("--dont_change_compression")
119+
args.add_all("--sources", jars)
120+
args.add("--normalize")
121+
args.add("--output", output_jar)
122+
actions.run(
123+
inputs = jars,
124+
outputs = [output_jar],
125+
executable = toolchain_info.single_jar,
126+
mnemonic = "SingleJar",
127+
arguments = [args],
128+
toolchain = TYPE,
129+
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
load("//src/main/starlark/core/compile:common.bzl", "JAVA_RUNTIME_TOOLCHAIN_TYPE", "JAVA_TOOLCHAIN_TYPE")
2+
load(
3+
"//src/main/starlark/core/options:opts.kotlinc.bzl",
4+
"KotlincOptions",
5+
)
6+
load(":compile.bzl", "build_deploy_jar", "compile_kotlin_for_jvm", "write_jvm_launcher")
7+
8+
KotlincJvmCompileInfo = provider(
9+
doc = "Necessary compilation info for compiling kotlin for the jvm",
10+
fields = {
11+
"jvm_target": "java target byte compatiblity.",
12+
"api_version": "kotlin api version.",
13+
"language_version": "Kotlin source version.",
14+
"compile_mnemonic": "Mnemonic for the compile action(s).",
15+
"executable_zip": "Executable for creating zip files.",
16+
"kotlinc": "kotlinc executable",
17+
"single_jar": "single jar executable",
18+
"java_stub_template": "Launcher template for running kotlin jars",
19+
"java_runtime": "Current kotlin java runtime",
20+
"kotlin_stdlib": "Koltin standard libs",
21+
},
22+
)
23+
24+
def _cli_toolchain(ctx):
25+
java_runtime = ctx.toolchains[JAVA_RUNTIME_TOOLCHAIN_TYPE].java_runtime
26+
java_toolchain = ctx.toolchains[JAVA_TOOLCHAIN_TYPE].java
27+
28+
toolchain_info = KotlincJvmCompileInfo(
29+
jvm_target = getattr(java_runtime, "version", 11), # default to 11 on old bazel. If it even works.
30+
api_version = ".".join(ctx.attr.api_version.split(".")[:2]),
31+
language_version = ".".join(ctx.attr.api_version.split(".")[:2]),
32+
executable_zip = ctx.attr.zip[DefaultInfo].files_to_run,
33+
kotlinc = ctx.attr.kotlinc[DefaultInfo].files_to_run,
34+
compile_mnemonic = "CliKotlinc",
35+
single_jar = java_toolchain.single_jar,
36+
java_stub_template = ctx.files.java_stub_template[0],
37+
java_runtime = java_runtime,
38+
kotlin_stdlib = java_common.merge([j[JavaInfo] for j in ctx.attr.kotlin_stdlibs]),
39+
)
40+
return [
41+
platform_common.ToolchainInfo(
42+
compile = _partial(compile_kotlin_for_jvm, toolchain_info = toolchain_info),
43+
launch = _partial(write_jvm_launcher, toolchain_info = toolchain_info),
44+
deploy = _partial(build_deploy_jar, toolchain_info = toolchain_info),
45+
),
46+
]
47+
48+
cli_toolchain = rule(
49+
doc = """The kotlin toolchain. This should not be created directly `define_kt_toolchain` should be used. The
50+
rules themselves define the toolchain using that macro.""",
51+
implementation = _cli_toolchain,
52+
attrs = {
53+
"language_version": attr.string(
54+
doc = "this is the -language_version flag [see](https://kotlinlang.org/docs/reference/compatibility.html)",
55+
),
56+
"api_version": attr.string(
57+
doc = "this is the -api_version flag [see](https://kotlinlang.org/docs/reference/compatibility.html).",
58+
),
59+
"zip": attr.label(
60+
executable = True,
61+
cfg = "exec",
62+
),
63+
"kotlinc": attr.label(
64+
executable = True,
65+
cfg = "exec",
66+
),
67+
"kotlin_stdlibs": attr.label_list(
68+
cfg = "exec",
69+
providers = [JavaInfo],
70+
),
71+
"java_stub_template": attr.label(
72+
cfg = "exec",
73+
default = Label("@bazel_tools//tools/java:java_stub_template.txt"),
74+
allow_single_file = True,
75+
),
76+
},
77+
toolchains = [
78+
JAVA_RUNTIME_TOOLCHAIN_TYPE,
79+
JAVA_TOOLCHAIN_TYPE,
80+
],
81+
)
82+
83+
def _partial(function, **defaults):
84+
def partial(**call):
85+
resolved = dict(defaults)
86+
resolved.update(call)
87+
return function(**resolved)
88+
89+
return partial
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
TYPE = "//src/main/starlark/core/compile:toolchain_type"
2+
3+
# Java toolchains
4+
JAVA_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:toolchain_type"
5+
JAVA_RUNTIME_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:runtime_toolchain_type"
6+
7+
KtJvmInfo = provider(
8+
fields = {
9+
"module_name": "the module name",
10+
"module_jars": "Jars comprising the module (logical compilation unit), a.k.a. associates",
11+
"exported_compiler_plugins": "compiler plugins to be invoked by targets depending on this.",
12+
"srcs": "the source files. [intelij-aspect]",
13+
"outputs": "output jars produced by this rule. [intelij-aspect]",
14+
"language_version": "version of kotlin used. [intellij-aspect]",
15+
"transitive_compile_time_jars": "Returns the transitive set of Jars required to build the target. [intellij-aspect]",
16+
"transitive_source_jars": "Returns the Jars containing source files of the current target and all of its transitive dependencies. [intellij-aspect]",
17+
"annotation_processing": "Generated annotation processing jars. [intellij-aspect]",
18+
"additional_generated_source_jars": "Returns additional Jars containing generated source files from kapt, ksp, etc. [bazel-bsp-aspect]",
19+
"all_output_jars": "Returns all the output Jars produced by this rule. [bazel-bsp-aspect]",
20+
},
21+
)

0 commit comments

Comments
 (0)