diff --git a/examples/deps/WORKSPACE b/examples/deps/WORKSPACE index c84a4edb9..93e896db2 100644 --- a/examples/deps/WORKSPACE +++ b/examples/deps/WORKSPACE @@ -70,7 +70,6 @@ load( android_sdk_repository( name = "androidsdk", - build_tools_version = "30.0.2", ) http_archive( diff --git a/kotlin/internal/jvm/android.bzl b/kotlin/internal/jvm/android.bzl index b43f3c686..165388cc8 100644 --- a/kotlin/internal/jvm/android.bzl +++ b/kotlin/internal/jvm/android.bzl @@ -35,6 +35,7 @@ def _kt_android_artifact( enable_data_binding = False, tags = [], exec_properties = None, + resource_files = None, **kwargs): """Delegates Android related build attributes to the native rules but uses the Kotlin builder to compile Java and Kotlin srcs. Returns a sequence of labels that a wrapping macro should export. @@ -45,16 +46,47 @@ def _kt_android_artifact( # TODO(bazelbuild/rules_kotlin/issues/273): This should be retrieved from a provider. base_deps = [_ANDROID_SDK_JAR] + deps - _android_library( - name = base_name, - visibility = ["//visibility:private"], - exports = base_deps, - deps = deps if enable_data_binding else [], - enable_data_binding = enable_data_binding, - tags = tags, - exec_properties = exec_properties, - **kwargs - ) + exported_target_labels = [kt_name] + if "kt_prune_transitive_deps_incompatible" in tags: + # TODO(https://github.com/bazelbuild/rules_kotlin/issues/556): replace with starlark + # buildifier: disable=native-android + _android_library( + name = base_name, + resource_files = resource_files, + exports = base_deps, + deps = deps if enable_data_binding else [], + enable_data_binding = enable_data_binding, + tags = tags, + visibility = ["//visibility:private"], + **kwargs + ) + exported_target_labels.append(base_name) + elif resource_files: + # TODO(https://github.com/bazelbuild/rules_kotlin/issues/556): replace with starlark + # buildifier: disable=native-android + # Do not export deps to avoid all upstream targets to be invalidated when ABI changes. + _android_library( + name = base_name, + resource_files = resource_files, + deps = deps, + custom_package = kwargs.get("custom_package", default = None), + manifest = kwargs.get("manifest", default = None), + enable_data_binding = enable_data_binding, + tags = tags, + visibility = ["//visibility:private"], + ) + exported_target_labels.append(base_name) + else: + # No need to export this target, as it's used exclusively internally + _android_library( + name = base_name, + exports = deps, + enable_data_binding = enable_data_binding, + tags = tags, + visibility = ["//visibility:private"], + **kwargs + ) + _kt_jvm_library( name = kt_name, srcs = srcs, diff --git a/kotlin/internal/jvm/compile.bzl b/kotlin/internal/jvm/compile.bzl index 80cbf7a81..1ef8e8c14 100644 --- a/kotlin/internal/jvm/compile.bzl +++ b/kotlin/internal/jvm/compile.bzl @@ -89,7 +89,7 @@ def _compiler_toolchains(ctx): java_runtime = find_java_runtime_toolchain(ctx, ctx.attr._host_javabase), ) -def _jvm_deps(toolchains, associated_targets, deps, runtime_deps = []): +def _jvm_deps(ctx, toolchains, associated_targets, deps, runtime_deps = []): """Encapsulates jvm dependency metadata.""" diff = _sets.intersection( _sets.copy_of([x.label for x in associated_targets]), @@ -101,16 +101,27 @@ def _jvm_deps(toolchains, associated_targets, deps, runtime_deps = []): ",\n ".join([" %s" % x for x in list(diff)]), ) dep_infos = [_java_info(d) for d in associated_targets + deps] + [toolchains.kt.jvm_stdlibs] + + # Reduced classpath, exclude transitive deps from compilation + if (toolchains.kt.experimental_prune_transitive_deps and + not "kt_experimental_prune_transitive_deps_incompatible" in ctx.attr.tags): + transitive = [ + d.compile_jars + for d in dep_infos + ] + else: + transitive = [ + d.compile_jars + for d in dep_infos + ] + [ + d.transitive_compile_time_jars + for d in dep_infos + ] + return struct( deps = dep_infos, compile_jars = depset( - transitive = [ - d.compile_jars - for d in dep_infos - ] + [ - d.transitive_compile_time_jars - for d in dep_infos - ], + transitive = transitive, ), runtime_deps = [_java_info(d) for d in runtime_deps], ) @@ -249,10 +260,6 @@ def _run_merge_jdeps_action(ctx, toolchains, jdeps, outputs, deps): args.add("--report_unused_deps", toolchains.kt.experimental_report_unused_deps) mnemonic = "JdepsMerge" - progress_message = "%s %%{label} { jdeps: %d }" % ( - mnemonic, - len(jdeps), - ) tools, input_manifests = ctx.resolve_tools( tools = [ @@ -263,6 +270,14 @@ def _run_merge_jdeps_action(ctx, toolchains, jdeps, outputs, deps): # For sandboxing to work, and for this action to be deterministic, the compile jars need to be passed as inputs inputs = depset(jdeps, transitive = [depset([], transitive = [dep.transitive_deps for dep in deps])]) + if "trace" in ctx.args.tags: + print(inputs) + progress_message = "%s %%{label} { jdeps: %s } { inputs: %s }" % ( + mnemonic, + jdeps, + inputs, + ) + ctx.actions.run( mnemonic = mnemonic, inputs = inputs, @@ -514,6 +529,7 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): srcs = _partitioned_srcs(ctx.files.srcs) associates = _associate_utils.get_associates(ctx) compile_deps = _jvm_deps( + ctx, toolchains, associates.targets, deps = ctx.attr.deps, diff --git a/kotlin/internal/toolchains.bzl b/kotlin/internal/toolchains.bzl index 9d1999361..b9ab122e4 100644 --- a/kotlin/internal/toolchains.bzl +++ b/kotlin/internal/toolchains.bzl @@ -90,6 +90,7 @@ def _kotlin_toolchain_impl(ctx): empty_jar = ctx.file._empty_jar, empty_jdeps = ctx.file._empty_jdeps, jacocorunner = ctx.attr.jacocorunner, + experimental_prune_transitive_deps = ctx.attr._experimental_prune_transitive_deps[BuildSettingInfo].value, ) return [ @@ -259,6 +260,12 @@ _kt_toolchain = rule( "jacocorunner": attr.label( default = Label("@bazel_tools//tools/jdk:JacocoCoverage"), ), + "_experimental_prune_transitive_deps": attr.label( + doc = """If enabled, compilation is performed against only direct dependencies. + Transitive deps required for compilation must be explicitly added. Using + kt_experimental_prune_transitive_deps_incompatible tag allows to exclude specific targets""", + default = Label("//kotlin/settings:experimental_prune_transitive_deps"), + ), "_jvm_emit_jdeps": attr.label(default = "//kotlin/settings:jvm_emit_jdeps"), }, implementation = _kotlin_toolchain_impl, diff --git a/kotlin/settings/BUILD.bazel b/kotlin/settings/BUILD.bazel index eb9e21a2e..e7128ea05 100644 --- a/kotlin/settings/BUILD.bazel +++ b/kotlin/settings/BUILD.bazel @@ -27,3 +27,11 @@ bool_flag( build_setting_default = True, visibility = ["//visibility:public"], ) + +# Kotlin strict deps can be enabled by setting the following value on the command line +# --@io_bazel_rules_kotlin//kotlin/settings:experimental_prune_transitive_deps=True +bool_flag( + name = "experimental_prune_transitive_deps", + build_setting_default = False, + visibility = ["//visibility:public"], +) diff --git a/kotlin/settings/BUILD.release.bazel b/kotlin/settings/BUILD.release.bazel index 2db3c2c47..cca157bf1 100644 --- a/kotlin/settings/BUILD.release.bazel +++ b/kotlin/settings/BUILD.release.bazel @@ -19,3 +19,11 @@ bool_flag( build_setting_default = True, # Upstream default behavior visibility = ["//visibility:public"], ) + +# Kotlin strict deps can be enabled by setting the following value on the command line +# --@io_bazel_rules_kotlin//kotlin/settings:experimental_prune_transitive_deps=True +bool_flag( + name = "experimental_prune_transitive_deps", + build_setting_default = False, + visibility = ["//visibility:public"], +) diff --git a/src/main/kotlin/io/bazel/kotlin/builder/cmd/MergeJdeps.kt b/src/main/kotlin/io/bazel/kotlin/builder/cmd/MergeJdeps.kt index 4e81b9fb9..75a81f044 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/cmd/MergeJdeps.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/cmd/MergeJdeps.kt @@ -19,6 +19,9 @@ package io.bazel.kotlin.builder.cmd import io.bazel.kotlin.builder.DaggerJdepsMergerComponent import io.bazel.worker.Worker +import java.nio.file.FileSystems +import java.nio.file.Files +import kotlin.io.path.walk import kotlin.system.exitProcess object MergeJdeps {