diff --git a/MODULE.bazel b/MODULE.bazel index 40af3bee4..3915c7f64 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -28,6 +28,14 @@ use_repo( "com_github_google_ksp", "com_github_jetbrains_kotlin", "com_github_jetbrains_kotlin_git", + "com_github_jetbrains_kotlin_native_linux_x86_64", + "com_github_jetbrains_kotlin_native_linux_x86_64_git", + "com_github_jetbrains_kotlin_native_macos_aarch64", + "com_github_jetbrains_kotlin_native_macos_aarch64_git", + "com_github_jetbrains_kotlin_native_macos_x86_64", + "com_github_jetbrains_kotlin_native_macos_x86_64_git", + "com_github_jetbrains_kotlin_native_windows_x86_64", + "com_github_jetbrains_kotlin_native_windows_x86_64_git", "com_github_pinterest_ktlint", "kotlin_build_tools_impl", "kotlinx_serialization_core_jvm", @@ -35,10 +43,12 @@ use_repo( "kotlinx_serialization_json_jvm", ) -register_toolchains("//src/main/starlark/core/compile/cli") +register_toolchains("//src/main/starlark/core/compile/cli:all") register_toolchains("//kotlin/internal:default_toolchain") +register_toolchains("//kotlin/internal/native:all") + # Development dependencies # TODO(bencodes) A bunch of these dependencies need to be marked as dev_dependencies but before we can do that # we need to sort out a few cases around how these rules are consumed in various ways. diff --git a/MODULE.release.bazel b/MODULE.release.bazel index 2968296d8..cbef74b74 100644 --- a/MODULE.release.bazel +++ b/MODULE.release.bazel @@ -31,6 +31,16 @@ use_repo( "kotlinx_serialization_json", "kotlinx_serialization_json_jvm", "kotlin_build_tools_impl", + "com_github_jetbrains_kotlin_native_linux_x86_64", + "com_github_jetbrains_kotlin_native_linux_x86_64_git", + "com_github_jetbrains_kotlin_native_macos_aarch64", + "com_github_jetbrains_kotlin_native_macos_aarch64_git", + "com_github_jetbrains_kotlin_native_macos_x86_64", + "com_github_jetbrains_kotlin_native_macos_x86_64_git", + "com_github_jetbrains_kotlin_native_windows_x86_64", + "com_github_jetbrains_kotlin_native_windows_x86_64_git", ) register_toolchains("//kotlin/internal:default_toolchain") + +register_toolchains("//kotlin/internal/native:all") diff --git a/docs/kotlin.md b/docs/kotlin.md index 1c96221eb..f0bd0721e 100755 --- a/docs/kotlin.md +++ b/docs/kotlin.md @@ -562,7 +562,7 @@ load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") kt_register_toolchains() -This macro registers the kotlin toolchain. +This macro registers the kotlin/JVM toolchain and the kotlin native toolchain. @@ -578,7 +578,9 @@ This macro registers the kotlin toolchain. load("@rules_kotlin//kotlin:repositories.doc.bzl", "kotlin_repositories") kotlin_repositories(is_bzlmod, compiler_repository_name, ksp_repository_name, compiler_release, - ksp_compiler_release) + ksp_compiler_release, kotlin_native_release_linux_x86_64, + kotlin_native_release_macos_x86_64, kotlin_native_release_macos_aarch64, + kotlin_native_release_windows_x86_64) Call this in the WORKSPACE file to setup the Kotlin rules. @@ -593,6 +595,10 @@ Call this in the WORKSPACE file to setup the Kotlin rules. | ksp_repository_name |

-

| `"com_github_google_ksp"` | | compiler_release | version provider from versions.bzl. | `struct(sha256 = "1ba08a8b45da99339a0601134cc037b54cf85e9bc0edbe76dcbd27c2d684a977", url_templates = ["https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip"], version = "2.1.21")` | | ksp_compiler_release | (internal) version provider from versions.bzl. | `struct(sha256 = "44e965bb067b2bb5cd9184dab2c3dea6e3eab747d341c07645bb4c88f09e49c8", url_templates = ["https://github.com/google/ksp/releases/download/{version}/artifacts.zip"], version = "2.1.21-2.0.1")` | +| kotlin_native_release_linux_x86_64 | (internal) version provider from versions.bzl | `struct(sha256 = "42fb88529b4039b6ac1961a137ccb1c79fc80315947f3ec31b56834c7ce20d0b", strip_prefix_template = "kotlin-native-prebuilt-linux-x86_64-{version}", url_templates = ["https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-linux-x86_64-{version}.tar.gz"], version = "2.1.21")` | +| kotlin_native_release_macos_x86_64 | (internal) version provider from versions.bzl | `struct(sha256 = "fc6b5979ec322be803bfac549661aaf0f8f7342aa3bd09008d471fff2757bbdf", strip_prefix_template = "kotlin-native-prebuilt-macos-x86_64-{version}", url_templates = ["https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-macos-x86_64-{version}.tar.gz"], version = "2.1.21")` | +| kotlin_native_release_macos_aarch64 |

-

| `struct(sha256 = "8df16175b962bc4264a5c3b32cb042d91458babbd093c0f36194dc4645f5fe2e", strip_prefix_template = "kotlin-native-prebuilt-macos-aarch64-{version}", url_templates = ["https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-macos-aarch64-{version}.tar.gz"], version = "2.1.21")` | +| kotlin_native_release_windows_x86_64 | (internal) version provider from versions.bzl | `struct(sha256 = "03301473bb9e68dadfdd265857a2a5913a147e700e345d32db73e0a21a2ffbfa", strip_prefix_template = "kotlin-native-prebuilt-windows-x86_64-{version}", url_templates = ["https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-windows-x86_64-{version}.zip"], version = "2.1.21")` | diff --git a/kotlin/compiler/BUILD b/kotlin/compiler/BUILD index f0698661d..17a08a0d0 100644 --- a/kotlin/compiler/BUILD +++ b/kotlin/compiler/BUILD @@ -15,6 +15,7 @@ load("//src/main/starlark/release:packager.bzl", "release_archive") # limitations under the License. load(":compiler.bzl", "kt_configure_compiler") load(":ksp.bzl", "kt_configure_ksp") +load(":native.bzl", "kt_configure_native_compiler") package(default_visibility = ["//visibility:public"]) @@ -24,6 +25,9 @@ kt_configure_compiler() # Configures the KSP plugins kt_configure_ksp() +# Configure the native compiler (used only for runfiles in KotlinBuilder) +kt_configure_native_compiler() + release_archive( name = "pkg", srcs = glob(["*.bzl"]), diff --git a/kotlin/compiler/BUILD.release.bazel b/kotlin/compiler/BUILD.release.bazel index f50885af0..1c40bc1b5 100644 --- a/kotlin/compiler/BUILD.release.bazel +++ b/kotlin/compiler/BUILD.release.bazel @@ -15,6 +15,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load(":compiler.bzl", "kt_configure_compiler") load(":ksp.bzl", "kt_configure_ksp") +load(":native.bzl", "kt_configure_native_compiler") package(default_visibility = ["//visibility:public"]) @@ -24,6 +25,9 @@ kt_configure_compiler() # Configures the KSP plugins kt_configure_ksp() +# Configure the native compiler (used only for runfiles in KotlinBuilder) +kt_configure_native_compiler() + bzl_library( name = "compiler", srcs = glob(["*.bzl"]), diff --git a/kotlin/compiler/compiler.bzl b/kotlin/compiler/compiler.bzl index 390cc9188..c60135414 100644 --- a/kotlin/compiler/compiler.bzl +++ b/kotlin/compiler/compiler.bzl @@ -25,18 +25,18 @@ KOTLIN_STDLIBS = [ "//kotlin/compiler:trove4j", ] -def _import_artifacts(artifacts, rule_kind): - _import_labels(artifacts.plugin, rule_kind) - _import_labels(artifacts.runtime, rule_kind) - _import_labels(artifacts.compile, rule_kind, neverlink = 1) +def _import_artifacts(artifacts, rule_kind, compiler_repo = _KT_COMPILER_REPO): + _import_labels(artifacts.plugin, rule_kind, compiler_repo) + _import_labels(artifacts.runtime, rule_kind, compiler_repo) + _import_labels(artifacts.compile, rule_kind, compiler_repo, neverlink = 1) -def _import_labels(labels, rule_kind, **rule_args): +def _import_labels(labels, rule_kind, compiler_repo, **rule_args): for (label, file) in labels.items(): if not file.endswith(".jar"): native.filegroup( name = label, srcs = [ - "@%s//:%s" % (_KT_COMPILER_REPO, label), + "@%s//:%s" % (compiler_repo, label), ], ) return @@ -46,10 +46,10 @@ def _import_labels(labels, rule_kind, **rule_args): args = dict(rule_args.items()) args["visibility"] = ["//visibility:public"] args["name"] = label - args["jars"] = ["@%s//:%s" % (_KT_COMPILER_REPO, label)] + args["jars"] = ["@%s//:%s" % (compiler_repo, label)] sources = label + "-sources" if sources in labels: - args["srcjar"] = "@%s//:%s" % (_KT_COMPILER_REPO, sources) + args["srcjar"] = "@%s//:%s" % (compiler_repo, sources) rule_kind(**args) def kt_configure_compiler(): diff --git a/kotlin/compiler/native.bzl b/kotlin/compiler/native.bzl new file mode 100644 index 000000000..e664cdc52 --- /dev/null +++ b/kotlin/compiler/native.bzl @@ -0,0 +1,17 @@ +load("//kotlin/internal:defs.bzl", "KT_NATIVE_COMPILER_REPO_PREFIX") + +def kt_configure_native_compiler(): + # Ideally this would not be needed here and be available from the toolchain + # but the builder uses java_binary and jvm_flags are propagated for the runfile paths to it directly + # so it doesn't seem straightforward to do that, so this alias target allows us + # to reference it in the data dependencies there + native.alias( + name = "kotlin-native", + actual = select({ + "@bazel_tools//src/conditions:linux_x86_64": "@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + "linux_x86_64//:kotlin-native", + "@bazel_tools//src/conditions:darwin": "@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + "macos_x86_64//:kotlin-native", + "@bazel_tools//src/conditions:windows": "@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + "windows_x86_64//:kotlin-native", + "@bazel_tools//src/conditions:darwin_arm64": "@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + "macos_aarch64//:kotlin-native", + }), + visibility = ["//src:__subpackages__"], + ) diff --git a/kotlin/internal/BUILD b/kotlin/internal/BUILD index 019803452..852584548 100644 --- a/kotlin/internal/BUILD +++ b/kotlin/internal/BUILD @@ -30,6 +30,7 @@ release_archive( deps = [ "//kotlin/internal/jvm:pkg", "//kotlin/internal/lint:pkg", + "//kotlin/internal/native:pkg", "//kotlin/internal/utils:pkg", ], ) @@ -41,6 +42,7 @@ bzl_library( deps = [ "//kotlin/internal/jvm", "//kotlin/internal/lint", + "//kotlin/internal/native", "//kotlin/internal/utils", "//src/main/starlark", "//src/main/starlark/core/compile", diff --git a/kotlin/internal/BUILD.release.bazel b/kotlin/internal/BUILD.release.bazel index 423b53698..d0861e7f6 100644 --- a/kotlin/internal/BUILD.release.bazel +++ b/kotlin/internal/BUILD.release.bazel @@ -24,6 +24,7 @@ bzl_library( deps = [ "//kotlin/internal/jvm", "//kotlin/internal/lint", + "//kotlin/internal/native", "//kotlin/internal/utils", "//src/main/starlark", ], diff --git a/kotlin/internal/defs.bzl b/kotlin/internal/defs.bzl index 8fc952f30..eca7ca9a8 100644 --- a/kotlin/internal/defs.bzl +++ b/kotlin/internal/defs.bzl @@ -16,6 +16,7 @@ load( _JAVA_RUNTIME_TOOLCHAIN_TYPE = "JAVA_RUNTIME_TOOLCHAIN_TYPE", _JAVA_TOOLCHAIN_TYPE = "JAVA_TOOLCHAIN_TYPE", _KtJvmInfo = "KtJvmInfo", + _KtKlibInfo = "KtKlibInfo", ) load( "//src/main/starlark/core/plugin:providers.bzl", @@ -27,6 +28,7 @@ load( # The Kotlin Toolchain type. TOOLCHAIN_TYPE = "%s" % Label("//kotlin/internal:kt_toolchain_type") +NATIVE_TOOLCHAIN_TYPE = "%s" % Label("//kotlin/internal/native:kt_native_toolchain_type") # Java toolchains JAVA_TOOLCHAIN_TYPE = _JAVA_TOOLCHAIN_TYPE @@ -35,6 +37,9 @@ JAVA_RUNTIME_TOOLCHAIN_TYPE = _JAVA_RUNTIME_TOOLCHAIN_TYPE # The name of the Kotlin compiler workspace. KT_COMPILER_REPO = "com_github_jetbrains_kotlin" +# The preifx of the Kotlin native compiler workspace name (will be suffixed with the platform) +KT_NATIVE_COMPILER_REPO_PREFIX = "com_github_jetbrains_kotlin_native" + # The name of the KSP compiler plugin workspace KSP_COMPILER_PLUGIN_REPO = "com_github_google_ksp" @@ -47,3 +52,5 @@ KspPluginInfo = _KspPluginInfo KtCompilerPluginOption = _KtCompilerPluginOption KtPluginConfiguration = _KtPluginConfiguration + +KtKlibInfo = _KtKlibInfo diff --git a/kotlin/internal/native/BUILD.bazel b/kotlin/internal/native/BUILD.bazel new file mode 100644 index 000000000..43844114c --- /dev/null +++ b/kotlin/internal/native/BUILD.bazel @@ -0,0 +1,24 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("//kotlin/internal/native:toolchains.bzl", "kt_configure_native_toolchains") +load("//src/main/starlark/release:packager.bzl", "release_archive") + +kt_configure_native_toolchains() + +release_archive( + name = "pkg", + srcs = glob(["*.bzl"]), + src_map = { + "BUILD.release.bazel": "BUILD.bazel", + }, +) + +bzl_library( + name = "native", + srcs = glob(["*.bzl"]), + visibility = ["//kotlin:__subpackages__"], + deps = [ + "//kotlin/internal/utils", + "//src/main/starlark/core/repositories/kotlin", + "@bazel_skylib//rules/directory", + ], +) diff --git a/kotlin/internal/native/BUILD.release.bazel b/kotlin/internal/native/BUILD.release.bazel new file mode 100644 index 000000000..3178ca320 --- /dev/null +++ b/kotlin/internal/native/BUILD.release.bazel @@ -0,0 +1,15 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load(":toolchains.bzl", "kt_configure_native_toolchains") + +kt_configure_native_toolchains() + +bzl_library( + name = "native", + srcs = glob(["*.bzl"]), + visibility = ["//kotlin:__subpackages__"], + deps = [ + "//kotlin/internal/utils", + "//src/main/starlark/core/repositories/kotlin", + "@bazel_skylib//rules/directory", + ], +) diff --git a/kotlin/internal/native/library.bzl b/kotlin/internal/native/library.bzl new file mode 100644 index 000000000..856ebb157 --- /dev/null +++ b/kotlin/internal/native/library.bzl @@ -0,0 +1,91 @@ +load("//kotlin/internal:defs.bzl", _KtKlibInfo = "KtKlibInfo", _NATIVE_TOOLCHAIN_TYPE = "NATIVE_TOOLCHAIN_TYPE", _TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE") +load("//kotlin/internal/utils:utils.bzl", "utils") + +def _kt_library_impl(ctx): + module_name = utils.derive_module_name(ctx) + builder_args = utils.init_args(ctx, "kt_library", module_name) + + klib = ctx.actions.declare_file("{}.klib".format(ctx.label.name)) + outputs = [klib] + + toolchains = ctx.toolchains[_TOOLCHAIN_TYPE] + + # Retrieve konan.home from the chosen toolchain's distribution + native_toolchain_info = ctx.toolchains[_NATIVE_TOOLCHAIN_TYPE].kotlin_native_info + konan_home = native_toolchain_info.konan_home + native_target = native_toolchain_info.target + + deps_klibs = [] + transitive_klibs = [] + runfiles = ctx.runfiles(files = [klib] + ctx.files.data) + + for dep in ctx.attr.deps: + deps_klibs.append(dep[_KtKlibInfo].klibs) + transitive_klibs.append(dep[_KtKlibInfo].transitive_klibs) + runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles) + + for d in ctx.attr.data: + runfiles = runfiles.merge(d[DefaultInfo].default_runfiles) + + libraries = depset(transitive = deps_klibs) + builder_args.add_all("--sources", ctx.files.srcs) + builder_inputs, _, input_manifests = ctx.resolve_command(tools = [toolchains.kotlinbuilder]) + + builder_args.add("--strict_kotlin_deps", "off") + builder_args.add("--reduced_classpath_mode", "off") + builder_args.add("--output_klib", klib.path) + builder_args.add("--kotlin_native_target", native_target) + + libraries = depset(transitive = deps_klibs) + builder_args.add_all("--klibs", libraries) + builder_args.add("--konan_home", konan_home.path) + + ctx.actions.run( + mnemonic = "KotlinKlibCompile", + inputs = depset(builder_inputs + ctx.files.srcs, transitive = [libraries, native_toolchain_info.konan_home_files]), + outputs = outputs, + executable = toolchains.kotlinbuilder.files_to_run.executable, + tools = [ + toolchains.kotlinbuilder.files_to_run, + ], + execution_requirements = {"supports-workers": "1"}, + arguments = [ctx.actions.args().add_all(toolchains.builder_args), builder_args], + progress_message = "Compiling Kotlin to Klib %%{label} { kt: %d }" % len(ctx.files.srcs), + input_manifests = input_manifests, + env = { + "REPOSITORY_NAME": utils.builder_workspace_name(ctx), + }, + ) + + return [ + DefaultInfo(files = depset(outputs, transitive = transitive_klibs + deps_klibs), runfiles = runfiles), + _KtKlibInfo( + klibs = depset(outputs), + transitive_klibs = depset(transitive = transitive_klibs + deps_klibs), + ), + ] + +kt_library = rule( + implementation = _kt_library_impl, + doc = """ +This rule is intended to leverage the new Kotlin IR backend to allow for compiling platform-independent Kotlin code +to be shared between Kotlin code for different platforms (JS/JVM/WASM etc.). It produces a klib file as the output. + """, + attrs = { + "srcs": attr.label_list( + doc = "A list of source files to be compiled to klib", + allow_files = [".kt"], + ), + "deps": attr.label_list( + doc = "A list of other kt_klib_library targets that this library depends on for compilation", + providers = [_KtKlibInfo], + ), + "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, + ), + }, + toolchains = [_TOOLCHAIN_TYPE, _NATIVE_TOOLCHAIN_TYPE], + provides = [_KtKlibInfo], +) diff --git a/kotlin/internal/native/toolchains.bzl b/kotlin/internal/native/toolchains.bzl new file mode 100644 index 000000000..49f16ff03 --- /dev/null +++ b/kotlin/internal/native/toolchains.bzl @@ -0,0 +1,86 @@ +load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") +load("//src/main/starlark/core/repositories/kotlin:artifacts.bzl", "KOTLIN_NATIVE_TARGETS") + +KotlinNativeToolchainInfo = provider(fields = { + "konan_home": "The path to konan.home directory", + "konan_properties": "The kokna.properties file corresponding to this distribution", + "konan_home_files": "A depset containing all the files in konan.home", + "targets": "A list of Kotlin native targets this toolchain supports", + "target": "The target chosen for compilation and to be passed as --target", +}) + +def _kt_native_toolchain_impl(ctx): + konan_home_info = ctx.attr.konan_home[DirectoryInfo] + konan_properties = konan_home_info.get_file("konan/konan.properties") + konan_home = konan_home_info + + return [ + platform_common.ToolchainInfo( + kotlin_native_info = KotlinNativeToolchainInfo( + konan_home = konan_home, + konan_home_files = depset(transitive = [konan_home_info.transitive_files]), + konan_properties = konan_properties, + targets = ctx.attr.targets, + target = ctx.attr.target, + ), + ), + ] + +kt_native_toolchain = rule( + implementation = _kt_native_toolchain_impl, + attrs = { + "konan_home": attr.label( + providers = [DirectoryInfo], + doc = "The directory containing konan.home", + ), + "targets": attr.string_list( + doc = "The list of targets supported by this toolchain. Each target can be passed to kotlinc-native with -target option", + default = [], + ), + "target": attr.string( + doc = "The Kotlin native target for this toolchain. This corresponds to the --target argument passed to the kotlin native compiler", + ), + }, + provides = [platform_common.ToolchainInfo], +) + +def kt_configure_native_toolchains(): + """Defines and registers the default toolchains for the kotlin-native compiler for all the platforms and targets supported.""" + native.toolchain_type( + name = "kt_native_toolchain_type", + visibility = ["//visibility:public"], + ) + + # Create toolchains for each exec platform and their supported targets + for exec_platform, targets in KOTLIN_NATIVE_TARGETS.items(): + exec_compatible_with = targets.exec_compatible_with + for target_constraint_tuple, kotlin_native_targets in targets.targets.items(): + target_os, target_cpu = target_constraint_tuple + + # Create a unique name for this toolchain + toolchain_suffix = "{}_to_{}_{}".format( + exec_platform, + Label(target_os).name, + Label(target_cpu).name, + ) + toolchain_name = "default_kt_native_toolchain_{}".format(toolchain_suffix) + toolchain_impl = "default_kt_native_{}".format(toolchain_suffix) + default_target = targets.targets[target_constraint_tuple][0] + + # Create the toolchain implementation + kt_native_toolchain( + name = toolchain_impl, + konan_home = "@com_github_jetbrains_kotlin_native_{}//:konan_home".format(exec_platform), + targets = kotlin_native_targets, + # TODO: provide a way to override this for this target platform with a config setting + target = default_target, + ) + + # Register the toolchain + native.toolchain( + name = toolchain_name, + exec_compatible_with = exec_compatible_with, + target_compatible_with = [target_os, target_cpu], + toolchain = ":" + toolchain_impl, + toolchain_type = ":kt_native_toolchain_type", + ) diff --git a/kotlin/internal/toolchains.bzl b/kotlin/internal/toolchains.bzl index b69a16a1b..72fdad3a4 100644 --- a/kotlin/internal/toolchains.bzl +++ b/kotlin/internal/toolchains.bzl @@ -304,11 +304,15 @@ _kt_toolchain = rule( ) _KT_DEFAULT_TOOLCHAIN = Label("//kotlin/internal:default_toolchain") +_KT_DEFAULT_NATIVE_TOOLCHAINS = Label("//kotlin/internal/native:all") def kt_register_toolchains(): - """This macro registers the kotlin toolchain.""" + """This macro registers the kotlin/JVM toolchain and the kotlin native toolchain.""" native.register_toolchains(str(_KT_DEFAULT_TOOLCHAIN)) + # Register all default native toolchains + native.register_toolchains(str(_KT_DEFAULT_NATIVE_TOOLCHAINS)) + # Evaluating the select in the context of bzl file to get its repository _DEBUG_SELECT = select({ str(Label("//kotlin/internal:builder_debug_trace")): ["trace"], diff --git a/src/main/kotlin/BUILD.release.bazel b/src/main/kotlin/BUILD.release.bazel index 2211fe94b..50680c987 100644 --- a/src/main/kotlin/BUILD.release.bazel +++ b/src/main/kotlin/BUILD.release.bazel @@ -42,6 +42,7 @@ java_binary( "//kotlin/compiler:jvm-abi-gen", "//kotlin/compiler:kotlin-annotation-processing", "//kotlin/compiler:kotlin-compiler", + "//kotlin/compiler:kotlin-native", "//kotlin/compiler:kotlin-reflect", "//kotlin/compiler:symbol-processing-api", "//kotlin/compiler:symbol-processing-cmdline", @@ -63,6 +64,7 @@ java_binary( "-D@rules_kotlin...jdeps-gen=$(rlocationpath //src/main/kotlin:jdeps-gen)", "-D@rules_kotlin...skip-code-gen=$(rlocationpath //src/main/kotlin:skip-code-gen)", "-D@rules_kotlin...compiler=$(rlocationpath //src/main/kotlin/io/bazel/kotlin/compiler)", + "-D@com_github_jetbrains_kotlin_native...kotlin-native=$(rlocationpath //kotlin/compiler:kotlin-native)", "-D@com_github_google_ksp...symbol-processing-api=$(rlocationpath //kotlin/compiler:symbol-processing-api)", "-D@com_github_google_ksp...symbol-processing-cmdline=$(rlocationpath //kotlin/compiler:symbol-processing-cmdline)", "-D@rules_kotlin..kotlin.compiler.kotlin-reflect=$(rlocationpath //kotlin/compiler:kotlin-reflect)", diff --git a/src/main/kotlin/io/bazel/kotlin/builder/KotlinBuilderComponent.java b/src/main/kotlin/io/bazel/kotlin/builder/KotlinBuilderComponent.java index 9cda68bd8..0d8ca9ae7 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/KotlinBuilderComponent.java +++ b/src/main/kotlin/io/bazel/kotlin/builder/KotlinBuilderComponent.java @@ -22,6 +22,7 @@ import io.bazel.kotlin.builder.tasks.CompileKotlin; import io.bazel.kotlin.builder.tasks.jvm.InternalCompilerPlugins; import io.bazel.kotlin.builder.tasks.jvm.KotlinJvmTaskExecutor; +import io.bazel.kotlin.builder.tasks.knative.KotlinNativeTaskExecutor; import io.bazel.kotlin.builder.toolchain.KotlinToolchain; import javax.inject.Singleton; @@ -33,6 +34,8 @@ public interface KotlinBuilderComponent { KotlinJvmTaskExecutor jvmTaskExecutor(); + KotlinNativeTaskExecutor nativeTaskExecutor(); + CompileKotlin work(); @Component.Builder diff --git a/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel index 98511d7b3..27a9804f7 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel +++ b/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel @@ -19,6 +19,7 @@ kt_bootstrap_binary( "//kotlin/compiler:jvm-abi-gen", "//kotlin/compiler:kotlin-annotation-processing", "//kotlin/compiler:kotlin-compiler", + "//kotlin/compiler:kotlin-native", "//kotlin/compiler:kotlin-reflect", "//kotlin/compiler:symbol-processing-api", "//kotlin/compiler:symbol-processing-cmdline", @@ -44,6 +45,7 @@ kt_bootstrap_binary( "-D@rules_kotlin...compiler=$(rlocationpath //src/main/kotlin/io/bazel/kotlin/compiler:compiler.jar)", "-D@com_github_google_ksp...symbol-processing-api=$(rlocationpath //kotlin/compiler:symbol-processing-api)", "-D@com_github_google_ksp...symbol-processing-cmdline=$(rlocationpath //kotlin/compiler:symbol-processing-cmdline)", + "-D@com_github_jetbrains_kotlin_native...kotlin-native=$(rlocationpath //kotlin/compiler:kotlin-native)", "-D@rules_kotlin..kotlin.compiler.kotlin-reflect=$(rlocationpath //kotlin/compiler:kotlin-reflect)", "-XX:-MaxFDLimit", ], diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt index e4ab30c5d..5dddf20a1 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt @@ -17,6 +17,7 @@ package io.bazel.kotlin.builder.tasks import io.bazel.kotlin.builder.tasks.jvm.KotlinJvmTaskExecutor +import io.bazel.kotlin.builder.tasks.knative.KotlinNativeTaskExecutor import io.bazel.kotlin.builder.toolchain.CompilationStatusException import io.bazel.kotlin.builder.toolchain.CompilationTaskContext import io.bazel.kotlin.builder.utils.ArgMap @@ -26,6 +27,7 @@ import io.bazel.kotlin.builder.utils.partitionJvmSources import io.bazel.kotlin.builder.utils.resolveNewDirectories import io.bazel.kotlin.model.CompilationTaskInfo import io.bazel.kotlin.model.JvmCompilationTask +import io.bazel.kotlin.model.KotlinNativeCompilationTask import io.bazel.kotlin.model.Platform import io.bazel.kotlin.model.RuleKind import io.bazel.worker.WorkerContext @@ -43,6 +45,7 @@ class KotlinBuilder @Inject internal constructor( private val jvmTaskExecutor: KotlinJvmTaskExecutor, + private val nativeTaskExecutor: KotlinNativeTaskExecutor, ) { companion object { @JvmStatic @@ -89,6 +92,10 @@ class KotlinBuilder INSTRUMENT_COVERAGE("--instrument_coverage"), KSP_GENERATED_JAVA_SRCJAR("--ksp_generated_java_srcjar"), BUILD_TOOLS_API("--build_tools_api"), + KLIBS("--klibs"), + OUTPUT_KLIB("--output_klib"), + KONAN_HOME("--konan_home"), + KOTLIN_NATIVE_TARGET("--kotlin_native_target"), } } @@ -105,6 +112,12 @@ class KotlinBuilder Platform.JVM, Platform.ANDROID, -> executeJvmTask(compileContext, taskContext.directory, argMap) + Platform.NATIVE_LIBRARY -> + executeKotlinNativeTask( + compileContext, + taskContext.directory, + argMap, + ) Platform.UNRECOGNIZED -> throw IllegalStateException( "unrecognized platform: ${compileContext.info}", ) @@ -146,8 +159,13 @@ class KotlinBuilder argMap.mandatorySingle(KotlinBuilderFlags.RULE_KIND).also { val splitRuleKind = it.split("_") require(splitRuleKind[0] == "kt") { "Invalid rule kind $it" } - platform = Platform.valueOf(splitRuleKind[1].uppercase()) ruleKind = RuleKind.valueOf(splitRuleKind.last().uppercase()) + platform = + when (it) { + // kt_library is a special case + "kt_library" -> Platform.NATIVE_LIBRARY + else -> Platform.valueOf(splitRuleKind[1].uppercase()) + } } moduleName = argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME).also { @@ -189,6 +207,47 @@ class KotlinBuilder jvmTaskExecutor.execute(context, task) } + private fun executeKotlinNativeTask( + context: CompilationTaskContext, + workingDir: Path, + argMap: ArgMap, + ) { + val task = buildKotlinNativeTask(context.info, workingDir, argMap) + context.whenTracing { printProto("kotlin native compile task input", task) } + nativeTaskExecutor.execute(context, task) + } + + private fun buildKotlinNativeTask( + info: CompilationTaskInfo, + workingDir: Path, + argMap: ArgMap, + ): KotlinNativeCompilationTask = + with(KotlinNativeCompilationTask.newBuilder()) { + this.info = info + with(directoriesBuilder) { + temp = workingDir.toString() + } + + with(infoBuilder.toolchainInfoBuilder.nativeBuilder) { + this.setKonanHome(argMap.mandatorySingle(KotlinBuilderFlags.KONAN_HOME)) + this.setKotlinNativeTarget( + argMap.mandatorySingle(KotlinBuilderFlags.KOTLIN_NATIVE_TARGET), + ) + } + + with(inputsBuilder) { + argMap.optional(KotlinBuilderFlags.KLIBS)?.let { + addAllLibraries(it) + } + addAllKotlinSources(argMap.mandatory(KotlinBuilderFlags.SOURCES)) + } + + with(outputsBuilder) { + this.setKlib(argMap.mandatorySingle(KotlinBuilderFlags.OUTPUT_KLIB)) + } + build() + } + private fun buildJvmTask( info: CompilationTaskInfo, workingDir: Path, diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/knative/KotlinNativeTaskExecutor.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/knative/KotlinNativeTaskExecutor.kt new file mode 100644 index 000000000..af3a20d64 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/knative/KotlinNativeTaskExecutor.kt @@ -0,0 +1,101 @@ +package io.bazel.kotlin.builder.tasks.knative + +import io.bazel.kotlin.builder.toolchain.CompilationTaskContext +import io.bazel.kotlin.builder.toolchain.KotlinToolchain +import io.bazel.kotlin.builder.utils.addAll +import io.bazel.kotlin.model.KotlinNativeCompilationTask +import java.nio.file.FileSystem +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.stream.Collectors +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists +import kotlin.io.path.pathString + +@Singleton +class KotlinNativeTaskExecutor + @Inject + constructor( + private val invoker: KotlinToolchain.K2NativeCompilerInvoker, + ) { + private val fileSystem: FileSystem = FileSystems.getDefault() + + fun execute( + context: CompilationTaskContext, + task: KotlinNativeCompilationTask, + ) { + task.compile(context) + } + + private fun KotlinNativeCompilationTask.workingDirectory(): Path = + fileSystem.getPath(directories.temp) + + private fun KotlinNativeCompilationTask.commonArgs(): MutableList { + val workDir = workingDirectory() + if (!workDir.exists()) { + workDir.toFile().mkdirs() + } + + val konanHome = this.info.toolchainInfo.native.konanHome + if (!Paths.get(konanHome).exists()) { + throw IllegalArgumentException("$konanHome doesn't point to konan.home or doesn't exist.") + } + + System.setProperty("konan.home", konanHome) + + val autoCacheDirectory = workingDirectory().resolve("native_auto_cache") + if (!autoCacheDirectory.exists()) { + autoCacheDirectory.toFile().mkdirs() + } + + val autoCacheFromDirectory = workingDirectory().resolve("native_auto_from") + if (!autoCacheFromDirectory.exists()) { + autoCacheFromDirectory.toFile().mkdirs() + } + + val execRoot = fileSystem.getPath(".") + return mutableListOf().apply { + addAll(passThroughFlagsList) + add("-Xklib-normalize-absolute-path") + // Avoid downloading any dependencies during the build + add("-Xoverride-konan-properties=airplaneMode=true") + add("-nostdlib") + + // the target for which we should compile libaries/binaries to + add("-target=${info.toolchainInfo.native.kotlinNativeTarget}") + + // Map paths in debug symbols to relative paths to execroot + add("-Xdebug-prefix-map=" + execRoot + "=.") + // Use relative paths in klibs + add("-Xklib-relative-path-base=" + execRoot) + addAll("-module-name=${info.moduleName}") + addAll(inputs.kotlinSourcesList.map { execRoot.resolve(it).absolutePathString() }) + } + } + + private fun KotlinNativeCompilationTask.compile(context: CompilationTaskContext) { + val args = commonArgs() + val klibOut = fileSystem.getPath(outputs.klib) + inputs.librariesList.forEach { library -> + args.addAll("-library=$library") + } + args.addAll("-produce", "library") + args.addAll("-o", klibOut.pathString.substringBeforeLast('.')) + context.whenTracing { printLines("klib compile args", args) } + + val outputDirectory = klibOut.parent + Files.createDirectories(outputDirectory) + + context.executeCompilerTask(args, invoker::compile) + context.whenTracing { + printLines( + "outputs", + Files.walk(outputDirectory).map { p -> p.toString() }.collect(Collectors.toList()), + ) + } + } + } diff --git a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt index d5ed414cf..e07a96f5b 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt @@ -83,6 +83,13 @@ class KotlinToolchain private constructor( ).toPath() } + private val KOTLIN_NATIVE by lazy { + BazelRunFiles + .resolveVerifiedFromProperty( + "@com_github_jetbrains_kotlin_native...kotlin-native", + ).toPath() + } + private val KSP_SYMBOL_PROCESSING_API by lazy { BazelRunFiles .resolveVerifiedFromProperty( @@ -150,6 +157,7 @@ class KotlinToolchain private constructor( createToolchain( JAVA_HOME, KOTLINC.verified().absoluteFile, + KOTLIN_NATIVE.verified().absoluteFile, COMPILER.verified().absoluteFile, BUILD_TOOLS_API.verified().absoluteFile, JVM_ABI_PLUGIN.verified().absoluteFile, @@ -167,6 +175,7 @@ class KotlinToolchain private constructor( fun createToolchain( javaHome: Path, kotlinc: File, + kotlinNative: File, buildTools: File, compiler: File, jvmAbiGenFile: File, @@ -182,6 +191,7 @@ class KotlinToolchain private constructor( KotlinToolchain( listOf( kotlinc, + kotlinNative, compiler, buildTools, // plugins *must* be preloaded. Not doing so causes class conflicts @@ -321,4 +331,14 @@ class KotlinToolchain private constructor( return KotlincInvoker(toolchain = toolchain, clazz = clazz) } } + + @Singleton + class K2NativeCompilerInvoker + @Inject + constructor( + toolchain: KotlinToolchain, + ) : KotlincInvoker( + toolchain, + "io.bazel.kotlin.compiler.BazelK2NativeCompiler", + ) } diff --git a/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2NativeCompiler.kt b/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2NativeCompiler.kt new file mode 100644 index 000000000..aabc0bd83 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2NativeCompiler.kt @@ -0,0 +1,37 @@ +/* + * 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 io.bazel.kotlin.compiler + +import org.jetbrains.kotlin.cli.bc.K2Native +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import org.jetbrains.kotlin.config.Services + +@Suppress("unused") +class BazelK2NativeCompiler { + fun exec( + errStream: java.io.PrintStream, + vararg args: String, + ): ExitCode { + System.setProperty("zip.handler.uses.crc.instead.of.timestamp", "true") + val delegate = K2Native() + val arguments = delegate.createArguments().also { delegate.parseArguments(args, it) } + val collector = + PrintingMessageCollector(errStream, MessageRenderer.PLAIN_RELATIVE_PATHS, arguments.verbose) + return delegate.exec(collector, Services.EMPTY, arguments) + } +} diff --git a/src/main/protobuf/kotlin_model.proto b/src/main/protobuf/kotlin_model.proto index cfae8edcc..ea6485747 100644 --- a/src/main/protobuf/kotlin_model.proto +++ b/src/main/protobuf/kotlin_model.proto @@ -41,8 +41,17 @@ message KotlinToolchainInfo { string jvm_target = 1; } + // Properties specific to the native compiler + message Native { + // The path to the konan directory in the distribution + string konan_home = 1; + // The kotlin native target for which to produce libraries/binaries + string kotlin_native_target = 2; + } + Common common = 1; Jvm jvm = 2; + Native native = 3; } enum RuleKind { @@ -55,6 +64,7 @@ enum RuleKind { enum Platform { JVM = 0; ANDROID = 1; + NATIVE_LIBRARY = 3; } // Common info about a Kotlin compilation task, this message is shared by all compilation tasks. @@ -175,3 +185,34 @@ message JvmCompilationTask { bool compile_kotlin = 6; bool instrument_coverage = 7; } + + +message KotlinNativeCompilationTask { + // Directories used by the builder. + message Directories { + // A temp directory that the compiler may use. + string temp = 4; + // Path to the konan distribution directory of the native compiler + string konan_home = 5; + } + + message Outputs { + string klib = 1; + } + + message Inputs { + // other klibs required for compilation + repeated string libraries = 1; + // One or more source files + repeated string kotlin_sources = 2; + } + + CompilationTaskInfo info = 1; + Outputs outputs = 3; + Inputs inputs = 4; + + // flags that should be passed through straight to the kotlin native compiler. + repeated string pass_through_flags = 5; + + Directories directories = 6; +} \ No newline at end of file diff --git a/src/main/starlark/core/compile/BUILD.bazel b/src/main/starlark/core/compile/BUILD.bazel index 6164469ff..273788046 100644 --- a/src/main/starlark/core/compile/BUILD.bazel +++ b/src/main/starlark/core/compile/BUILD.bazel @@ -8,6 +8,13 @@ toolchain_type( ], ) +toolchain_type( + name = "native_toolchain_type", + visibility = [ + "//visibility:public", + ], +) + bzl_library( name = "compile", srcs = [ diff --git a/src/main/starlark/core/compile/cli/BUILD.bazel b/src/main/starlark/core/compile/cli/BUILD.bazel index 46ed8b78b..b401255d6 100644 --- a/src/main/starlark/core/compile/cli/BUILD.bazel +++ b/src/main/starlark/core/compile/cli/BUILD.bazel @@ -3,6 +3,7 @@ load("@rules_java//java:java_import.bzl", "java_import") load("//kotlin/compiler:compiler.bzl", _KOTLIN_STDLIBS = "KOTLIN_STDLIBS") load("//src/main/starlark/core/compile:common.bzl", "TYPE") load("//src/main/starlark/core/repositories:versions.bzl", "versions") +load(":native.bzl", "define_native_cli_toolchains") load(":toolchain.bzl", "cli_toolchain") java_import( @@ -35,3 +36,5 @@ toolchain( toolchain = ":cli_toolchain", toolchain_type = TYPE, ) + +define_native_cli_toolchains() diff --git a/src/main/starlark/core/compile/cli/native.bzl b/src/main/starlark/core/compile/cli/native.bzl new file mode 100644 index 000000000..32f24e8ad --- /dev/null +++ b/src/main/starlark/core/compile/cli/native.bzl @@ -0,0 +1,73 @@ +load("@rules_java//java/common:java_info.bzl", "JavaInfo") +load("//kotlin/internal:defs.bzl", "KT_NATIVE_COMPILER_REPO_PREFIX", "KtJvmInfo") +load("//src/main/starlark/core/compile:common.bzl", "NATIVE_TYPE") +load("//src/main/starlark/core/repositories/kotlin:artifacts.bzl", "KOTLIN_NATIVE_TARGETS") + +KotlinNativeCompileInfo = provider( + doc = "Provies the necessary info about the Kotlin native CLI toolchain", + fields = { + "native_java_infos": "A list of JavaInfo objects that represent the kotlin-native CLI toolchain", + "jvm_flags": "A list of JVM flags to be used with kotlinc-native CLI toolchain", + }, +) + +def _kotlin_native_cli_toolchain_impl(ctx): + native_java_infos = [j[JavaInfo] for j in ctx.attr.native_jars] + return [ + platform_common.ToolchainInfo( + kotlin_native_compile_info = KotlinNativeCompileInfo( + native_java_infos = native_java_infos, + jvm_flags = ctx.attr.jvm_flags, + ), + ), + ] + +kotlin_native_cli_toolchain = rule( + implementation = _kotlin_native_cli_toolchain_impl, + attrs = { + "native_jars": attr.label_list( + doc = "One ore more jars that are required to run the kotlinc-native CLI", + providers = [KtJvmInfo], + ), + "jvm_flags": attr.string_list( + doc = "A list of JVM flags to be used in binaries to use the kotlinc-native CLI toolchain", + default = [], + ), + }, +) + +def define_native_cli_toolchains(): + for exec_platform, targets in KOTLIN_NATIVE_TARGETS.items(): + exec_compatible_with = targets.exec_compatible_with + + for target_constraint_tuple in targets.targets.keys(): + target_os, target_cpu = target_constraint_tuple + + # Create a unique name for this toolchain + toolchain_suffix = "{}_to_{}_{}".format( + exec_platform, + Label(target_os).name, + Label(target_cpu).name, + ) + toolchain_name = "default_kt_native_toolchain_{}".format(toolchain_suffix) + toolchain_impl = "default_kt_native_{}".format(toolchain_suffix) + + kotlin_native = Label("@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + exec_platform + "//:kotlin-native") + + # Create the toolchain implementation + kotlin_native_cli_toolchain( + name = toolchain_impl, + native_jars = [ + kotlin_native, + "@" + KT_NATIVE_COMPILER_REPO_PREFIX + "_" + exec_platform + "//:trove4j", + ], + ) + + # Register the toolchain + native.toolchain( + name = toolchain_name, + exec_compatible_with = exec_compatible_with, + target_compatible_with = [target_os, target_cpu], + toolchain = ":" + toolchain_impl, + toolchain_type = NATIVE_TYPE, + ) diff --git a/src/main/starlark/core/compile/common.bzl b/src/main/starlark/core/compile/common.bzl index 7b892e496..7e03caf4f 100644 --- a/src/main/starlark/core/compile/common.bzl +++ b/src/main/starlark/core/compile/common.bzl @@ -1,4 +1,5 @@ TYPE = "//src/main/starlark/core/compile:toolchain_type" +NATIVE_TYPE = "//src/main/starlark/core/compile:native_toolchain_type" # Java toolchains JAVA_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/jdk:toolchain_type") @@ -19,3 +20,11 @@ KtJvmInfo = provider( "all_output_jars": "Returns all the output Jars produced by this rule. [bazel-bsp-aspect]", }, ) + +KtKlibInfo = provider( + fields = { + "module_name": "the module_name", + "klibs": "the klibs provided by the output of compilation", + "transitive_klibs": "Returns the transitive set of klibs required to build the target", + }, +) diff --git a/src/main/starlark/core/compile/rules.bzl b/src/main/starlark/core/compile/rules.bzl index 668ee7778..2c92733f3 100644 --- a/src/main/starlark/core/compile/rules.bzl +++ b/src/main/starlark/core/compile/rules.bzl @@ -1,5 +1,5 @@ load("@rules_java//java:defs.bzl", "JavaInfo") -load(":common.bzl", "KtJvmInfo", "TYPE") +load(":common.bzl", "KtJvmInfo", "NATIVE_TYPE", "TYPE") _COMMON_ATTRS = { "srcs": attr.label_list( @@ -59,6 +59,9 @@ _COMMON_ATTRS = { def _kt_jvm_library_impl(ctx): kt_tools = ctx.toolchains[TYPE] + native_toolchain = ctx.toolchains[NATIVE_TYPE] + native_java_infos = native_toolchain.kotlin_native_compile_info.native_java_infos + class_jar = ctx.outputs.class_jar source_jar = ctx.outputs.source_jar java_info_deps = [d[JavaInfo] for d in ctx.attr.deps if JavaInfo in d] @@ -66,7 +69,7 @@ def _kt_jvm_library_impl(ctx): kt_tools.compile( actions = ctx.actions, srcs = ctx.files.srcs, - dep_jars = depset(transitive = [j.compile_jars for j in java_info_deps]), + dep_jars = depset(transitive = [j.compile_jars for j in java_info_deps] + [j.compile_jars for j in native_java_infos]), class_jar = class_jar, output_srcjar = source_jar, module_name = module_name, @@ -121,6 +124,7 @@ _kt_jvm_library = rule( attrs = _COMMON_ATTRS, toolchains = [ TYPE, + NATIVE_TYPE, ], provides = [JavaInfo, KtJvmInfo], ) @@ -135,10 +139,13 @@ def core_kt_jvm_library(name, **kwargs): def _kt_jvm_binary_impl(ctx): kt_tools = ctx.toolchains[TYPE] + native_toolchain = ctx.toolchains[NATIVE_TYPE] + native_java_infos = native_toolchain.kotlin_native_compile_info.native_java_infos + native_jvm_flags = native_toolchain.kotlin_native_compile_info.jvm_flags providers = _kt_jvm_library_impl(ctx) java_info_deps = [d[JavaInfo] for d in ctx.attr.deps if JavaInfo in d] - runtime_jars = depset([ctx.outputs.class_jar], transitive = [j.transitive_runtime_jars for j in java_info_deps]) + runtime_jars = depset([ctx.outputs.class_jar], transitive = [j.transitive_runtime_jars for j in java_info_deps] + [j.transitive_runtime_jars for j in native_java_infos]) executable = ctx.outputs.executable launch_runfiles = kt_tools.launch( @@ -147,7 +154,7 @@ def _kt_jvm_binary_impl(ctx): actions = ctx.actions, path_separator = ctx.configuration.host_path_separator, workspace_prefix = ctx.workspace_name + "/", - jvm_flags = " ".join([ctx.expand_location(f, ctx.attr.data) for f in ctx.attr.jvm_flags]), + jvm_flags = " ".join([ctx.expand_location(f, ctx.attr.data) for f in ctx.attr.jvm_flags + native_jvm_flags]), runtime_jars = runtime_jars, ) @@ -185,6 +192,7 @@ _kt_jvm_binary = rule( }, toolchains = [ TYPE, + NATIVE_TYPE, ], executable = True, ) diff --git a/src/main/starlark/core/repositories/BUILD b/src/main/starlark/core/repositories/BUILD index 869f9e74d..550ebae31 100644 --- a/src/main/starlark/core/repositories/BUILD +++ b/src/main/starlark/core/repositories/BUILD @@ -19,10 +19,14 @@ release_archive( srcs = [ "BUILD.com_github_google_ksp.bazel", "BUILD.com_github_jetbrains_kotlin.bazel", + "BUILD.com_github_jetbrains_kotlin_native.bazel", + "BUILD.kotlin-native_capabilities.bazel", "BUILD.kotlin_capabilities.bazel", "bzlmod_impl.bzl", + "capabilities.bzl", "compiler.bzl", "ksp.bzl", + "native.bzl", "versions.bzl", ], src_map = { diff --git a/src/main/starlark/core/repositories/BUILD.com_github_jetbrains_kotlin_native.bazel b/src/main/starlark/core/repositories/BUILD.com_github_jetbrains_kotlin_native.bazel new file mode 100644 index 000000000..e04b88613 --- /dev/null +++ b/src/main/starlark/core/repositories/BUILD.com_github_jetbrains_kotlin_native.bazel @@ -0,0 +1,63 @@ +# Copyright 2025 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("@bazel_skylib//rules/directory:directory.bzl", "directory") +load("@rules_kotlin//kotlin/internal/jvm:jvm.bzl", "kt_jvm_import") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "bin", + srcs = glob(["bin/**"]), +) + +directory( + name = "konan_home", + srcs = glob([ + "konan/**", + "klib/**", + ]), +) + +filegroup( + name = "konan_properties", + srcs = ["konan/konan.properties"], +) + +filegroup( + name = "stdlib", + srcs = glob(["klib/common/stdlib/**"]), +) + +# Have a filegroup for the kotlin-native jars that will be explicitly aliased +[ + filegroup( + name = name.replace(".", "_"), + srcs = glob(["" + name]), + ) + for name in glob(["konan/lib/**"]) +] + +kt_jvm_import( + name = "kotlin-native", + jars = [ + "konan/lib/kotlin-native.jar", + ], +) + +kt_jvm_import( + name = "trove4j", + jars = [ + "konan/lib/trove4j.jar", + ], +) diff --git a/src/main/starlark/core/repositories/BUILD.kotlin-native_capabilities.bazel b/src/main/starlark/core/repositories/BUILD.kotlin-native_capabilities.bazel new file mode 100644 index 000000000..5c4c0d026 --- /dev/null +++ b/src/main/starlark/core/repositories/BUILD.kotlin-native_capabilities.bazel @@ -0,0 +1,41 @@ +# Copyright 2025 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("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +package(default_visibility = ["//visibility:public"]) + +bzl_library( + name = "capabilities", + srcs = glob(["*.bzl"]), +) + +alias( + name = "konan_home", + actual = "@$git_repo$//:konan_home", +) + +alias( + name = "konan_properties", + actual = "@$git_repo$//:konan_properties", +) + +alias( + name = "kotlin-native", + actual = "@$git_repo$//:kotlin-native", +) + +alias( + name = "trove4j", + actual = "@$git_repo$//:trove4j", +) diff --git a/src/main/starlark/core/repositories/capabilities.bzl b/src/main/starlark/core/repositories/capabilities.bzl new file mode 100644 index 000000000..4346fee81 --- /dev/null +++ b/src/main/starlark/core/repositories/capabilities.bzl @@ -0,0 +1,98 @@ +load("//src/main/starlark/core/repositories/kotlin:templates.bzl", "TEMPLATES") + +def _kotlin_capabilities_impl(repository_ctx): + """Creates the kotlinc or kotlin-native repository.""" + attr = repository_ctx.attr + repository_ctx.file( + "WORKSPACE", + content = """workspace(name = "%s")""" % attr.name, + ) + repository_ctx.template( + "BUILD.bazel", + attr.template, + executable = False, + substitutions = { + "$git_repo$": attr.git_repository_name, + }, + ) + repository_ctx.template( + "artifacts.bzl", + attr._artifacts_template, + executable = False, + ) + template = _get_capability_template( + attr.compiler_version, + [repository_ctx.path(ct) for ct in attr._capability_templates], + ) + repository_ctx.template( + "capabilities.bzl", + template, + executable = False, + ) + +def _coerce_int(string_value): + digits = "".join([ + string_value[i] + for i in range(len(string_value)) + if string_value[i].isdigit() + ]) + return 0 if not digits else int(digits) + +def _version(version_string): + return tuple([ + _coerce_int(segment) + for segment in version_string.split(".", 3) + ]) + +def _parse_version(basename): + if "capabilities" not in basename: + return None + version_string = basename[len("capabilities_"):basename.find(".bzl")] + return _version(version_string) + +def _get_capability_template(compiler_version, templates): + version_index = {} + target = _version(compiler_version) + if len(target) > 2: + target = target[0:2] + for template in templates: + version = _parse_version(template.basename) + if not version: + continue + + if target == version: + return template + version_index[version] = template + + last_version = sorted(version_index.keys(), reverse = True)[0] + + # After latest version, chosen by major revision + if target[0] >= last_version[0]: + return version_index[last_version] + + # Legacy + return version_index[(0, 0, 0)] + +kotlin_capabilities_repository = repository_rule( + implementation = _kotlin_capabilities_impl, + attrs = { + "git_repository_name": attr.string( + doc = "Name of the repository containing kotlin compiler libraries", + ), + "compiler_version": attr.string( + doc = "compiler version", + ), + "_capability_templates": attr.label_list( + doc = "List of compiler capability templates.", + default = TEMPLATES, + ), + "template": attr.label( + doc = "repository build file template", + default = ":BUILD.kotlin_capabilities.bazel", + ), + "_artifacts_template": attr.label( + doc = "kotlinc artifacts template", + default = "//src/main/starlark/core/repositories/kotlin:artifacts.bzl", + ), + }, +) diff --git a/src/main/starlark/core/repositories/compiler.bzl b/src/main/starlark/core/repositories/compiler.bzl index ac45b3147..640cfde78 100644 --- a/src/main/starlark/core/repositories/compiler.bzl +++ b/src/main/starlark/core/repositories/compiler.bzl @@ -2,7 +2,7 @@ Defines kotlin compiler repositories. """ -load("//src/main/starlark/core/repositories/kotlin:templates.bzl", "TEMPLATES") +load(":capabilities.bzl", "kotlin_capabilities_repository") def _kotlin_compiler_impl(repository_ctx): attr = repository_ctx.attr @@ -17,103 +17,6 @@ def _kotlin_compiler_impl(repository_ctx): executable = False, ) -def _kotlin_capabilities_impl(repository_ctx): - """Creates the kotlinc repository.""" - attr = repository_ctx.attr - repository_ctx.file( - "WORKSPACE", - content = """workspace(name = "%s")""" % attr.name, - ) - repository_ctx.template( - "BUILD.bazel", - attr._template, - executable = False, - substitutions = { - "$git_repo$": attr.git_repository_name, - }, - ) - repository_ctx.template( - "artifacts.bzl", - attr._artifacts_template, - executable = False, - ) - template = _get_capability_template( - attr.compiler_version, - [repository_ctx.path(ct) for ct in attr._capability_templates], - ) - repository_ctx.template( - "capabilities.bzl", - template, - executable = False, - ) - -def _coerce_int(string_value): - digits = "".join([ - string_value[i] - for i in range(len(string_value)) - if string_value[i].isdigit() - ]) - return 0 if not digits else int(digits) - -def _version(version_string): - return tuple([ - _coerce_int(segment) - for segment in version_string.split(".", 3) - ]) - -def _parse_version(basename): - if "capabilities" not in basename: - return None - version_string = basename[len("capabilities_"):basename.find(".bzl")] - return _version(version_string) - -def _get_capability_template(compiler_version, templates): - version_index = {} - target = _version(compiler_version) - if len(target) > 2: - target = target[0:2] - for template in templates: - version = _parse_version(template.basename) - if not version: - continue - - if target == version: - return template - version_index[version] = template - - last_version = sorted(version_index.keys(), reverse = True)[0] - - # After latest version, chosen by major revision - if target[0] >= last_version[0]: - return version_index[last_version] - - # Legacy - return version_index[(0, 0, 0)] - -kotlin_capabilities_repository = repository_rule( - implementation = _kotlin_capabilities_impl, - attrs = { - "git_repository_name": attr.string( - doc = "Name of the repository containing kotlin compiler libraries", - ), - "compiler_version": attr.string( - doc = "compiler version", - ), - "_capability_templates": attr.label_list( - doc = "List of compiler capability templates.", - default = TEMPLATES, - ), - "_template": attr.label( - doc = "repository build file template", - default = ":BUILD.kotlin_capabilities.bazel", - ), - "_artifacts_template": attr.label( - doc = "kotlinc artifacts template", - default = "//src/main/starlark/core/repositories/kotlin:artifacts.bzl", - ), - }, -) - kotlin_compiler_git_repository = repository_rule( implementation = _kotlin_compiler_impl, attrs = { diff --git a/src/main/starlark/core/repositories/initialize.bzl b/src/main/starlark/core/repositories/initialize.bzl index 81d298bad..a83fabacd 100644 --- a/src/main/starlark/core/repositories/initialize.bzl +++ b/src/main/starlark/core/repositories/initialize.bzl @@ -31,12 +31,20 @@ ksp_version = _ksp_version def kotlin_repositories( is_bzlmod = False, compiler_release = versions.KOTLIN_CURRENT_COMPILER_RELEASE, - ksp_compiler_release = versions.KSP_CURRENT_COMPILER_PLUGIN_RELEASE): + ksp_compiler_release = versions.KSP_CURRENT_COMPILER_PLUGIN_RELEASE, + kotlin_native_release_linux_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_LINUX_X86_64, + kotlin_native_release_macos_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_X86_64, + kotlin_native_release_macos_aarch64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_AARCH64, + kotlin_native_release_windows_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_WINDOWS_X86_64): """Call this in the WORKSPACE file to setup the Kotlin rules. Args: compiler_release: (internal) version provider from versions.bzl. ksp_compiler_release: (internal) version provider from versions.bzl. + kotlin_native_release_linux_x86_64: (internal) version provider from versions.bzl + kotlin_native_release_macos_x86_64: (internal) version provider from versions.bzl + kotlin_native_release_macos_aarch_64: (internal) version provider from versions.bzl + kotlin_native_release_windows_x86_64: (internal) version provider from versions.bzl """ - _release_kotlin_repositories(is_bzlmod = is_bzlmod, compiler_release = compiler_release, ksp_compiler_release = ksp_compiler_release) + _release_kotlin_repositories(is_bzlmod = is_bzlmod, compiler_release = compiler_release, ksp_compiler_release = ksp_compiler_release, kotlin_native_release_linux_x86_64 = kotlin_native_release_linux_x86_64, kotlin_native_release_macos_x86_64 = kotlin_native_release_macos_x86_64, kotlin_native_release_windows_x86_64 = kotlin_native_release_windows_x86_64, kotlin_native_release_macos_aarch64 = kotlin_native_release_macos_aarch64) kt_configure() diff --git a/src/main/starlark/core/repositories/initialize.release.bzl b/src/main/starlark/core/repositories/initialize.release.bzl index f834c95cc..fb183ae56 100644 --- a/src/main/starlark/core/repositories/initialize.release.bzl +++ b/src/main/starlark/core/repositories/initialize.release.bzl @@ -25,9 +25,11 @@ load( "//kotlin/internal:defs.bzl", _KSP_COMPILER_PLUGIN_REPO = "KSP_COMPILER_PLUGIN_REPO", _KT_COMPILER_REPO = "KT_COMPILER_REPO", + _KT_NATIVE_COMPILER_REPO_PREFIX = "KT_NATIVE_COMPILER_REPO_PREFIX", ) load(":compiler.bzl", "kotlin_compiler_repository") load(":ksp.bzl", "ksp_compiler_plugin_repository") +load(":native.bzl", "kotlin_native_compiler_repository") load(":versions.bzl", "version", _versions = "versions") versions = _versions @@ -39,7 +41,11 @@ def kotlin_repositories( compiler_repository_name = _KT_COMPILER_REPO, ksp_repository_name = _KSP_COMPILER_PLUGIN_REPO, compiler_release = versions.KOTLIN_CURRENT_COMPILER_RELEASE, - ksp_compiler_release = versions.KSP_CURRENT_COMPILER_PLUGIN_RELEASE): + ksp_compiler_release = versions.KSP_CURRENT_COMPILER_PLUGIN_RELEASE, + kotlin_native_release_linux_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_LINUX_X86_64, + kotlin_native_release_macos_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_X86_64, + kotlin_native_release_macos_aarch64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_AARCH64, + kotlin_native_release_windows_x86_64 = versions.KOTLIN_NATIVE_CURRENT_RELEASE_WINDOWS_X86_64): """Call this in the WORKSPACE file to setup the Kotlin rules. Args: @@ -48,6 +54,10 @@ def kotlin_repositories( configured_repository_name: for the default versioned kt_* rules repository. If None, no versioned repository is created. ksp_compiler_release: (internal) version provider from versions.bzl. + kotlin_native_release_linux_x86_64: (internal) version provider from versions.bzl + kotlin_native_release_macos_x86_64: (internal) version provider from versions.bzl + kotlin_native_release_macos_aarch_64: (internal) version provider from versions.bzl + kotlin_native_release_windows_x86_64: (internal) version provider from versions.bzl """ kotlin_compiler_repository( @@ -57,6 +67,38 @@ def kotlin_repositories( compiler_version = compiler_release.version, ) + kotlin_native_compiler_repository( + name = _KT_NATIVE_COMPILER_REPO_PREFIX + "_linux_x86_64", + compiler_version = kotlin_native_release_linux_x86_64.version, + urls = [url.format(version = kotlin_native_release_linux_x86_64.version) for url in kotlin_native_release_linux_x86_64.url_templates], + sha256 = kotlin_native_release_linux_x86_64.sha256, + strip_prefix = kotlin_native_release_linux_x86_64.strip_prefix_template.format(version = kotlin_native_release_linux_x86_64.version), + ) + + kotlin_native_compiler_repository( + name = _KT_NATIVE_COMPILER_REPO_PREFIX + "_macos_x86_64", + compiler_version = kotlin_native_release_macos_x86_64.version, + urls = [url.format(version = kotlin_native_release_macos_x86_64.version) for url in kotlin_native_release_macos_x86_64.url_templates], + sha256 = kotlin_native_release_macos_x86_64.sha256, + strip_prefix = kotlin_native_release_macos_x86_64.strip_prefix_template.format(version = kotlin_native_release_macos_x86_64.version), + ) + + kotlin_native_compiler_repository( + name = _KT_NATIVE_COMPILER_REPO_PREFIX + "_macos_aarch64", + compiler_version = kotlin_native_release_macos_aarch64.version, + urls = [url.format(version = kotlin_native_release_macos_aarch64.version) for url in kotlin_native_release_macos_aarch64.url_templates], + sha256 = kotlin_native_release_macos_aarch64.sha256, + strip_prefix = kotlin_native_release_macos_aarch64.strip_prefix_template.format(version = kotlin_native_release_macos_aarch64.version), + ) + + kotlin_native_compiler_repository( + name = _KT_NATIVE_COMPILER_REPO_PREFIX + "_windows_x86_64", + compiler_version = kotlin_native_release_windows_x86_64.version, + urls = [url.format(version = kotlin_native_release_windows_x86_64.version) for url in kotlin_native_release_windows_x86_64.url_templates], + sha256 = kotlin_native_release_windows_x86_64.sha256, + strip_prefix = kotlin_native_release_windows_x86_64.strip_prefix_template.format(version = kotlin_native_release_windows_x86_64.version), + ) + ksp_compiler_plugin_repository( name = ksp_repository_name, urls = [url.format(version = ksp_compiler_release.version) for url in ksp_compiler_release.url_templates], diff --git a/src/main/starlark/core/repositories/kotlin/artifacts.bzl b/src/main/starlark/core/repositories/kotlin/artifacts.bzl index a837b1cfa..6c9ba1b8f 100644 --- a/src/main/starlark/core/repositories/kotlin/artifacts.bzl +++ b/src/main/starlark/core/repositories/kotlin/artifacts.bzl @@ -70,6 +70,79 @@ KOTLINC_ARTIFACTS = struct( ), ) +KOTLIN_NATIVE_TARGETS = { + "linux_x86_64": struct( + exec_compatible_with = ["@platforms//os:linux", "@platforms//cpu:x86_64"], + # the targets have been extracted here manually by either running kotlinc-native -list-targets for the relevant distribution + # or listing entries under /targets/, and then map Bazel platforms for it so that we can create the relevant toolchains + # with the right target_compatible_with + targets = { + ("@platforms//os:android", "@platforms//cpu:armv7"): ["android_arm32"], + ("@platforms//os:android", "@platforms//cpu:arm64"): ["android_arm64"], + ("@platforms//os:android", "@platforms//cpu:x86_64"): ["android_x64"], + ("@platforms//os:linux", "@platforms//cpu:armv7"): ["linux_arm32_hfp"], + ("@platforms//os:linux", "@platforms//cpu:x86_64"): ["linux_x64"], + ("@platforms//os:windows", "@platforms//cpu:x86_64"): ["mingw_x64"], + }, + ), + "macos_x86_64": struct( + exec_compatible_with = ["@platforms//os:macos", "@platforms//cpu:x86_64"], + targets = { + ("@platforms//os:android", "@platforms//cpu:armv7"): ["android_arm32"], + ("@platforms//os:android", "@platforms//cpu:arm64"): ["android_arm64"], + ("@platforms//os:android", "@platforms//cpu:x86_64"): ["android_x64"], + ("@platforms//os:android", "@platforms//cpu:x86_32"): ["android_x86"], + ("@platforms//os:ios", "@platforms//cpu:arm64"): ["ios_arm64", "ios_simulator_arm64"], + ("@platforms//os:ios", "@platforms//cpu:x86_64"): ["ios_x64"], + ("@platforms//os:linux", "@platforms//cpu:armv7"): ["linux_arm32_hfp"], + ("@platforms//os:linux", "@platforms//cpu:arm64"): ["linux_arm64"], + ("@platforms//os:linux", "@platforms//cpu:x86_64"): ["linux_x64"], + ("@platforms//os:macos", "@platforms//cpu:arm64"): ["macos_arm64"], + ("@platforms//os:macos", "@platforms//cpu:x86_64"): ["macos_x64"], + ("@platforms//os:windows", "@platforms//cpu:x86_64"): ["mingw_x64"], + ("@platforms//os:tvos", "@platforms//cpu:arm64"): ["tvos_arm64", "tvos_simulator_arm64"], + ("@platforms//os:tvos", "@platforms//cpu:x86_64"): ["tvos_x64"], + ("@platforms//os:watchos", "@platforms//cpu:armv7k"): ["watchos_arm32"], + ("@platforms//os:watchos", "@platforms//cpu:arm64"): ["watchos_arm64", "watchos_simulator_arm64"], + ("@platforms//os:watchos", "@platforms//cpu:arm64_32"): ["watchos_device_arm64"], + ("@platforms//os:watchos", "@platforms//cpu:x86_64"): ["watchos_x64"], + }, + ), + "macos_aarch64": struct( + exec_compatible_with = ["@platforms//os:macos", "@platforms//cpu:arm64"], + targets = { + ("@platforms//os:linux", "@platforms//cpu:x86_64"): ["linux_x64"], + ("@platforms//os:linux", "@platforms//cpu:arm64"): ["linux_arm64"], + ("@platforms//os:windows", "@platforms//cpu:x86_64"): ["mingw_x64"], + ("@platforms//os:android", "@platforms//cpu:x86_32"): ["android_x86"], + ("@platforms//os:android", "@platforms//cpu:x86_64"): ["android_x64"], + ("@platforms//os:android", "@platforms//cpu:armv7"): ["android_arm32"], + ("@platforms//os:android", "@platforms//cpu:arm64"): ["android_arm64"], + ("@platforms//os:macos", "@platforms//cpu:x86_64"): ["macos_x64"], + ("@platforms//os:macos", "@platforms//cpu:arm64"): ["macos_arm64"], + ("@platforms//os:ios", "@platforms//cpu:arm64"): ["ios_arm64", "ios_simulator_arm64"], + ("@platforms//os:ios", "@platforms//cpu:x86_64"): ["ios_x64"], + ("@platforms//os:watchos", "@platforms//cpu:armv7k"): ["watchos_arm32"], + ("@platforms//os:watchos", "@platforms//cpu:arm64"): ["watchos_arm64", "watchos_simulator_arm64"], + ("@platforms//os:watchos", "@platforms//cpu:arm64_32"): ["watchos_device_arm64"], + ("@platforms//os:watchos", "@platforms//cpu:x86_64"): ["watchos_x64"], + ("@platforms//os:tvos", "@platforms//cpu:arm64"): ["tvos_arm64", "tvos_simulator_arm64"], + ("@platforms//os:tvos", "@platforms//cpu:x86_64"): ["tvos_x64"], + }, + ), + "windows_x86_64": struct( + exec_compatible_with = ["@platforms//os:windows", "@platforms//cpu:x86_64"], + targets = { + ("@platforms//os:windows", "@platforms//cpu:x86_64"): ["mingw_x64"], + ("@platforms//os:android", "@platforms//cpu:armv7"): ["android_arm32"], + ("@platforms//os:android", "@platforms//cpu:arm64"): ["android_arm64"], + ("@platforms//os:android", "@platforms//cpu:x86_32"): ["android_x86"], + ("@platforms//os:android", "@platforms//cpu:x86_64"): ["android_x64"], + ("@platforms//os:linux", "@platforms//cpu:x86_64"): ["linux_64"], + }, + ), +} + KOTLINC_ARTIFACT_LIST = { label: file for lang in ["jvm", "core"] diff --git a/src/main/starlark/core/repositories/native.bzl b/src/main/starlark/core/repositories/native.bzl new file mode 100644 index 000000000..d04ae22ba --- /dev/null +++ b/src/main/starlark/core/repositories/native.bzl @@ -0,0 +1,52 @@ +load(":capabilities.bzl", "kotlin_capabilities_repository") + +_CAPABILITIES_BUILD_TEMPLATE = Label("BUILD.kotlin-native_capabilities.bazel") + +def _kotlin_native_compiler_repository_impl(repository_ctx): + attr = repository_ctx.attr + repository_ctx.download_and_extract( + attr.urls, + sha256 = attr.sha256, + stripPrefix = attr.strip_prefix, + ) + repository_ctx.template( + "BUILD.bazel", + attr._template, + executable = False, + ) + +kotlin_native_compiler_repository_rule = repository_rule( + implementation = _kotlin_native_compiler_repository_impl, + attrs = { + "urls": attr.string_list( + mandatory = True, + doc = "A list of urls for the kotlin-native compiler", + ), + "sha256": attr.string( + mandatory = True, + doc = "the sha256 of the kotlin-native tar/zip for this platform/version.", + ), + "_template": attr.label( + doc = "The build file template for the kotlin-native repository", + default = ":BUILD.com_github_jetbrains_kotlin_native.bazel", + ), + "strip_prefix": attr.string( + mandatory = True, + doc = "The prefix to be stripped from the extracted kotlin-native compiler archive, and is platform specific", + ), + }, +) + +def kotlin_native_compiler_repository(name, compiler_version, **kwargs): + git_repo = name + "_git" + kotlin_native_compiler_repository_rule( + name = git_repo, + **kwargs + ) + + kotlin_capabilities_repository( + name = name, + git_repository_name = git_repo, + compiler_version = compiler_version, + template = _CAPABILITIES_BUILD_TEMPLATE, + ) diff --git a/src/main/starlark/core/repositories/versions.bzl b/src/main/starlark/core/repositories/versions.bzl index 814ae02b5..4f80685a9 100644 --- a/src/main/starlark/core/repositories/versions.bzl +++ b/src/main/starlark/core/repositories/versions.bzl @@ -10,6 +10,8 @@ version = provider( }, ) +_DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION = "2.1.21" + def _use_repository(name, version, rule, **kwargs): http_archive_arguments = dict(kwargs) http_archive_arguments["sha256"] = version.sha256 @@ -74,12 +76,44 @@ versions = struct( sha256 = "5ba1ac917a06b0f02daaa60d10abbedd2220d60216af670c67a45b91c74cf8bb", ), KOTLIN_CURRENT_COMPILER_RELEASE = version( - version = "2.1.21", + version = _DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION, url_templates = [ "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip", ], sha256 = "1ba08a8b45da99339a0601134cc037b54cf85e9bc0edbe76dcbd27c2d684a977", ), + KOTLIN_NATIVE_CURRENT_RELEASE_LINUX_X86_64 = version( + version = _DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION, + url_templates = [ + "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-linux-x86_64-{version}.tar.gz", + ], + sha256 = "42fb88529b4039b6ac1961a137ccb1c79fc80315947f3ec31b56834c7ce20d0b", + strip_prefix_template = "kotlin-native-prebuilt-linux-x86_64-{version}", + ), + KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_X86_64 = version( + version = _DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION, + url_templates = [ + "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-macos-x86_64-{version}.tar.gz", + ], + sha256 = "fc6b5979ec322be803bfac549661aaf0f8f7342aa3bd09008d471fff2757bbdf", + strip_prefix_template = "kotlin-native-prebuilt-macos-x86_64-{version}", + ), + KOTLIN_NATIVE_CURRENT_RELEASE_MACOS_AARCH64 = version( + version = _DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION, + url_templates = [ + "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-macos-aarch64-{version}.tar.gz", + ], + sha256 = "8df16175b962bc4264a5c3b32cb042d91458babbd093c0f36194dc4645f5fe2e", + strip_prefix_template = "kotlin-native-prebuilt-macos-aarch64-{version}", + ), + KOTLIN_NATIVE_CURRENT_RELEASE_WINDOWS_X86_64 = version( + version = _DEFAULT_KOTLIN_COMPILER_RELEASE_VERSION, + url_templates = [ + "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-native-prebuilt-windows-x86_64-{version}.zip", + ], + sha256 = "03301473bb9e68dadfdd265857a2a5913a147e700e345d32db73e0a21a2ffbfa", + strip_prefix_template = "kotlin-native-prebuilt-windows-x86_64-{version}", + ), KSP_CURRENT_COMPILER_PLUGIN_RELEASE = version( version = "2.1.21-2.0.1", url_templates = [ diff --git a/src/test/data/native/BUILD.bazel b/src/test/data/native/BUILD.bazel new file mode 100644 index 000000000..a152999a8 --- /dev/null +++ b/src/test/data/native/BUILD.bazel @@ -0,0 +1,26 @@ +load("//kotlin/internal/native:library.bzl", "kt_library") + +kt_library( + name = "basic", + srcs = ["basic/Hello.kt"], +) + +kt_library( + name = "deps_greeting", + srcs = ["deps/data/Greeting.kt"], +) + +kt_library( + name = "deps_main", + srcs = ["deps/Main.kt"], + deps = [":deps_greeting"], +) + +filegroup( + name = "klib_tests", + srcs = [ + "deps_main", + ":basic", + ], + visibility = ["//src/test:__subpackages__"], +) diff --git a/src/test/data/native/basic/Hello.kt b/src/test/data/native/basic/Hello.kt new file mode 100644 index 000000000..e4a5e73f3 --- /dev/null +++ b/src/test/data/native/basic/Hello.kt @@ -0,0 +1,19 @@ +package basic + +fun main() { + val rawNames = listOf("alice", "", "Bob", "charlie", " ", "dave") + + // Clean up names: trim, filter blanks + val cleanedNames = rawNames + .map { it.trim() } + .filter { it.isNotEmpty() } + .sortedBy { it.lowercase() } + + // Capitalize each name + val capitalizedNames = cleanedNames.map { it.replaceFirstChar { c -> c.uppercaseChar() } } + + // Print numbered list + capitalizedNames.forEachIndexed { index, name -> + println("${index + 1}. $name") + } +} diff --git a/src/test/data/native/deps/Main.kt b/src/test/data/native/deps/Main.kt new file mode 100644 index 000000000..8507ec84f --- /dev/null +++ b/src/test/data/native/deps/Main.kt @@ -0,0 +1,9 @@ +package main + +import data.createGreeting + +fun main() { + val name = "Alice" + val greeting = createGreeting(name) + println(greeting) +} diff --git a/src/test/data/native/deps/data/Greeting.kt b/src/test/data/native/deps/data/Greeting.kt new file mode 100644 index 000000000..a19859e1e --- /dev/null +++ b/src/test/data/native/deps/data/Greeting.kt @@ -0,0 +1,5 @@ +package data + +fun createGreeting(name: String): String { + return "Hello, $name!" +} diff --git a/src/test/kotlin/io/bazel/kotlin/BUILD b/src/test/kotlin/io/bazel/kotlin/BUILD index 2ac635bd5..35d684395 100644 --- a/src/test/kotlin/io/bazel/kotlin/BUILD +++ b/src/test/kotlin/io/bazel/kotlin/BUILD @@ -68,6 +68,14 @@ kt_rules_e2e_test( data = ["//src/test/data/jvm/ksp"], ) +kt_rules_e2e_test( + name = "KotlinKlibAssertionTest", + srcs = ["KotlinKlibAssertionTest.kt"], + data = [ + "//src/test/data/native:klib_tests", + ], +) + test_suite( name = "assertion_tests", tests = [ diff --git a/src/test/kotlin/io/bazel/kotlin/KotlinAssertionTestCase.kt b/src/test/kotlin/io/bazel/kotlin/KotlinAssertionTestCase.kt index 07156823d..5be14a690 100644 --- a/src/test/kotlin/io/bazel/kotlin/KotlinAssertionTestCase.kt +++ b/src/test/kotlin/io/bazel/kotlin/KotlinAssertionTestCase.kt @@ -26,6 +26,7 @@ import java.time.ZoneId import java.util.concurrent.TimeUnit import java.util.jar.JarEntry import java.util.jar.JarFile +import java.util.zip.ZipFile import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -37,6 +38,9 @@ class TestCaseFailedException(name: String? = null, description: String? = null, cause ) +// klib is just a zip file with specific entries +typealias KlibZipFile = ZipFile + abstract class KotlinAssertionTestCase(root: String) : BasicAssertionTestCase() { private lateinit var currentFile: File @@ -70,6 +74,28 @@ abstract class KotlinAssertionTestCase(root: String) : BasicAssertionTestCase() runTestCase(name, description) { JarFile(currentFile).op() } } + protected fun klibTestCase( + name: String, + description: String? = null, + op: KlibZipFile.() -> Unit + ) { + currentFile = testRunfileRoot.resolve(name).toFile() + check(currentFile.exists()) { + "testFile $name did not exist in test case root $testRunfileRoot" + } + runTestCase(name, description) { + KlibZipFile(currentFile).op() + } + } + + protected fun KlibZipFile.assertContainsEntries(vararg want: String) { + val got = this.entries().asSequence().map { it.name }.toSet() + val missing = want.toSet() - got + check(missing.isEmpty()) { + "Entries Missing: $missing \nFrom: $got" + } + } + protected fun JarFile.assertContainsEntries(vararg want: String) { val got = this.entries().asSequence().map { it.name }.toSet() val missing = want.toSet() - got diff --git a/src/test/kotlin/io/bazel/kotlin/KotlinKlibAssertionTest.kt b/src/test/kotlin/io/bazel/kotlin/KotlinKlibAssertionTest.kt new file mode 100644 index 000000000..978d9fb0d --- /dev/null +++ b/src/test/kotlin/io/bazel/kotlin/KotlinKlibAssertionTest.kt @@ -0,0 +1,28 @@ +package io.bazel.kotlin + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class KotlinKlibAssertionTest : KotlinAssertionTestCase("src/test/data/native") { + @Test + fun testBasicKlibIsProduced() { + klibTestCase("basic.klib", "kt_lib_library produces klib with stdlib usage") { + assertContainsEntries( + "default/linkdata/package_basic/0_basic.knm", + "default/manifest", + ) + } + } + + @Test + fun testDepsKlibIsProduced() { + klibTestCase("deps_main.klib", "kt_lib_library produces klib with stdlib usage") { + assertContainsEntries( + "default/linkdata/package_main/0_main.knm", + "default/manifest", + ) + } + } +} diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java index ff2292288..b4278df49 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java @@ -235,6 +235,7 @@ static KotlinToolchain toolchainForTest() { return KotlinToolchain.createToolchain( javaHome, new File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-compiler").singleCompileJar()), + new File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-native").singleCompileJar()), new File(Deps.Dep.fromLabel("@kotlin_build_tools_impl//jar").singleCompileJar()), new File(Deps.Dep.fromLabel("//src/main/kotlin/io/bazel/kotlin/compiler:compiler.jar").singleCompileJar()), new File(Deps.Dep.fromLabel("//kotlin/compiler:jvm-abi-gen").singleCompileJar()), diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java index bab333ef6..7caee8757 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java @@ -23,7 +23,6 @@ import io.bazel.kotlin.builder.toolchain.CompilationTaskContext; import io.bazel.kotlin.model.CompilationTaskInfo; import io.bazel.kotlin.model.JvmCompilationTask; -import io.bazel.kotlin.model.KotlinToolchainInfo; import java.util.EnumSet; import java.util.HashSet; diff --git a/src/test/kotlin/io/bazel/kotlin/defs.bzl b/src/test/kotlin/io/bazel/kotlin/defs.bzl index fe5e2111c..4252c8e99 100644 --- a/src/test/kotlin/io/bazel/kotlin/defs.bzl +++ b/src/test/kotlin/io/bazel/kotlin/defs.bzl @@ -39,6 +39,7 @@ def kt_rules_test(name, **kwargs): "//kotlin/compiler:annotations", "//kotlin/compiler:jvm-abi-gen", "//kotlin/compiler:kotlin-compiler", + "//kotlin/compiler:kotlin-native", "//kotlin/compiler:kotlin-stdlib", "//kotlin/compiler:kotlin-stdlib-jdk7", "//kotlin/compiler:kotlin-stdlib-jdk8", diff --git a/src/test/starlark/internal/native/BUILD.bazel b/src/test/starlark/internal/native/BUILD.bazel new file mode 100644 index 000000000..ebf6e36ba --- /dev/null +++ b/src/test/starlark/internal/native/BUILD.bazel @@ -0,0 +1,3 @@ +load(":kt_library_tests.bzl", "kt_library_test_suite") + +kt_library_test_suite(name = "library_tests") diff --git a/src/test/starlark/internal/native/kt_library_tests.bzl b/src/test/starlark/internal/native/kt_library_tests.bzl new file mode 100644 index 000000000..5a80ae9de --- /dev/null +++ b/src/test/starlark/internal/native/kt_library_tests.bzl @@ -0,0 +1,97 @@ +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//kotlin/internal:defs.bzl", "KtKlibInfo") +load("//kotlin/internal/native:library.bzl", "kt_library") + +def _common_assertions(env, target): + # Assertions common to all kt_library tests + target_subject = env.expect.that_target(target) + target_subject.has_provider(KtKlibInfo) + + action_subject = target_subject.action_named("KotlinKlibCompile") + action_subject.argv().contains_at_least(["--rule_kind", "kt_library", "--output_klib", "--konan_home"]) + +def _test_kt_library_basic_impl(env, target): + _common_assertions(env, target) + target_subject = env.expect.that_target(target) + action_subject = target_subject.action_named("KotlinKlibCompile") + action_subject.inputs().contains("src/test/starlark/internal/native/Basic.kt") + + target_subject.runfiles().contains("_main/src/test/starlark/internal/native/basic.klib") + +def _test_kt_library_basic(name): + kt_library(name = "basic", srcs = ["Basic.kt"], tags = ["manual"]) + + analysis_test(name, target = "basic", impl = _test_kt_library_basic_impl) + +def _test_kt_library_deps_impl(env, target): + _common_assertions(env, target) + + target_subject = env.expect.that_target(target) + action_subject = target_subject.action_named("KotlinKlibCompile") + action_subject.inputs().contains("src/test/starlark/internal/native/Second.kt") + + # the klib from first compilation is passed as input + action_subject.inputs().contains("src/test/starlark/internal/native/first.klib") + + # and it's passed through the arguments + action_subject.argv().contains_at_least(["--klibs"]) + + # Transitive outputs are propagated in runfiles + target_subject.runfiles().contains_at_least(["_main/src/test/starlark/internal/native/first.klib", "_main/src/test/starlark/internal/native/second.klib"]) + +def _test_kt_library_deps(name): + kt_library(name = "first", srcs = ["First.kt"], tags = ["manual"]) + + kt_library(name = "second", srcs = ["Second.kt"], deps = [":first"], tags = ["manual"]) + + analysis_test(name, target = "second", impl = _test_kt_library_deps_impl) + +def _test_kt_library_runfiles_impl(env, target): + _common_assertions(env, target) + + target_subject = env.expect.that_target(target) + target_subject.data_runfiles().contains_at_least(["_main/src/test/starlark/internal/native/foo.txt"]) + +def _test_kt_library_runfiles(name): + native.filegroup( + name = "runfiles1", + srcs = ["foo.txt"], + tags = ["manual"], + ) + kt_library(name = "lib_with_data", srcs = ["First.kt"], data = [":runfiles1"], tags = ["manual"]) + + analysis_test(name, target = "lib_with_data", impl = _test_kt_library_runfiles_impl) + +def _test_kt_library_transitive_runfiles_impl(env, target): + _common_assertions(env, target) + + target_subject = env.expect.that_target(target) + target_subject.data_runfiles().contains_at_least(["_main/src/test/starlark/internal/native/foo.txt"]) + +def _test_kt_library_transitive_runfiles(name): + native.filegroup( + name = "runfiles2", + srcs = ["foo.txt"], + tags = ["manual"], + ) + kt_library(name = "lib2_with_data", srcs = ["First.kt"], data = [":runfiles2"], tags = ["manual"]) + + kt_library(name = "final_lib", srcs = ["Second.kt"], deps = [":lib2_with_data"], tags = ["manual"]) + + analysis_test(name, target = "final_lib", impl = _test_kt_library_transitive_runfiles_impl) + +def kt_library_test_suite(name): + test_suite( + name = name, + tests = [ + # assert compilation of single target with no deps works + _test_kt_library_basic, + # Assert compilation with deps works and transitive outputs are propagated + _test_kt_library_deps, + # Assert runfiles are available + _test_kt_library_runfiles, + # Assert transitive runfiles are propagated + _test_kt_library_transitive_runfiles, + ], + )