diff --git a/kotlin/internal/jvm/compile.bzl b/kotlin/internal/jvm/compile.bzl index d7bd39d93..04fde4503 100644 --- a/kotlin/internal/jvm/compile.bzl +++ b/kotlin/internal/jvm/compile.bzl @@ -536,6 +536,9 @@ def _run_kt_builder_action( omit_if_empty = True, ) + if toolchains.kt.experimental_use_public_only_abi_jars == True: + args.add("--public_only_in_abi_jar", "true") + args.add("--build_kotlin", build_kotlin) progress_message = "%s %%{label} { kt: %d, java: %d, srcjars: %d } for %s" % ( diff --git a/kotlin/internal/toolchains.bzl b/kotlin/internal/toolchains.bzl index 3394e8f84..743c8f349 100644 --- a/kotlin/internal/toolchains.bzl +++ b/kotlin/internal/toolchains.bzl @@ -86,6 +86,7 @@ def _kotlin_toolchain_impl(ctx): "supports-multiplex-workers": "1" if ctx.attr.experimental_multiplex_workers else "0", }, experimental_use_abi_jars = ctx.attr.experimental_use_abi_jars, + experimental_use_public_only_abi_jars = ctx.attr.experimental_use_public_only_abi_jars, experimental_strict_kotlin_deps = ctx.attr.experimental_strict_kotlin_deps, experimental_report_unused_deps = ctx.attr.experimental_report_unused_deps, experimental_reduce_classpath_mode = ctx.attr.experimental_reduce_classpath_mode, @@ -202,6 +203,15 @@ _kt_toolchain = rule( `kt_abi_plugin_incompatible`""", default = False, ), + "experimental_use_public_only_abi_jars": attr.bool( + doc = """Compile using public only abi jars. + This effectively applies the following two compiler plugin options. + plugin:org.jetbrains.kotlin.jvm.abi:treatInternalAsPrivate=true + plugin:org.jetbrains.kotlin.jvm.abi:removePrivateClasses=true + Can be disabled for an individual target using the tag. + `kt_abi_plugin_incompatible`""", + default = False, + ), "experimental_strict_kotlin_deps": attr.string( doc = "Report strict deps violations", default = "off", @@ -288,6 +298,7 @@ def define_kt_toolchain( api_version = None, jvm_target = None, experimental_use_abi_jars = False, + experimental_use_public_only_abi_jars = False, experimental_strict_kotlin_deps = None, experimental_report_unused_deps = None, experimental_reduce_classpath_mode = None, @@ -314,6 +325,7 @@ def define_kt_toolchain( _NOEXPERIMENTAL_USE_ABI_JARS: False, "//conditions:default": experimental_use_abi_jars, }), + experimental_use_public_only_abi_jars = experimental_use_public_only_abi_jars, experimental_multiplex_workers = experimental_multiplex_workers, experimental_strict_kotlin_deps = experimental_strict_kotlin_deps, experimental_report_unused_deps = experimental_report_unused_deps, 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 ea1c03797..2822e2a40 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt @@ -77,6 +77,7 @@ class KotlinBuilder DEBUG("--kotlin_debug_tags"), TASK_ID("--kotlin_task_id"), ABI_JAR("--abi_jar"), + PUBLIC_ABI_JAR("--public_only_in_abi_jar"), GENERATED_JAVA_SRC_JAR("--generated_java_srcjar"), GENERATED_JAVA_STUB_JAR("--kapt_generated_stub_jar"), GENERATED_CLASS_JAR("--kapt_generated_class_jar"), @@ -161,6 +162,9 @@ class KotlinBuilder argMap.mandatorySingle(KotlinBuilderFlags.LANGUAGE_VERSION) strictKotlinDeps = argMap.mandatorySingle(KotlinBuilderFlags.STRICT_KOTLIN_DEPS) reducedClasspathMode = argMap.mandatorySingle(KotlinBuilderFlags.REDUCED_CLASSPATH_MODE) + argMap.optionalSingle(KotlinBuilderFlags.PUBLIC_ABI_JAR)?.let { includePublicOnlyInAbiJar = + it == "true" + } this } diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt index b1e07f733..9947befbf 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt @@ -87,6 +87,10 @@ class KotlinJvmTaskExecutor .notEmpty { plugin(plugins.jvmAbiGen) { flag("outputDir", directories.abiClasses) + if(info.includePublicOnlyInAbiJar) { + flag("removePrivateClasses", "true") + flag("treatInternalAsPrivate", "true") + } } given(outputs.jar).empty { plugin(plugins.skipCodeGen) diff --git a/src/main/protobuf/kotlin_model.proto b/src/main/protobuf/kotlin_model.proto index 3fb6ca2da..94495c7a9 100644 --- a/src/main/protobuf/kotlin_model.proto +++ b/src/main/protobuf/kotlin_model.proto @@ -81,6 +81,8 @@ message CompilationTaskInfo { string strict_kotlin_deps = 10; // Optimize classpath by removing dependencies not required for compilation string reduced_classpath_mode = 11; + // Include only public symbols in abi.jar + bool include_public_only_in_abi_jar = 12; } // Nested messages not marked with stable could be refactored. diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java index 4d0534efa..b36e9bb50 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java @@ -228,6 +228,11 @@ public TaskBuilder outputAbiJar() { return this; } + public TaskBuilder publicOnlyAbiJar() { + taskBuilder.getInfoBuilder().setIncludePublicOnlyInAbiJar(true); + return this; + } + public TaskBuilder generatedSourceJar() { taskBuilder.getOutputsBuilder() .setGeneratedJavaSrcJar(instanceRoot().resolve("gen-src.jar").toAbsolutePath().toString()); diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmAbiTest.java b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmAbiTest.java index 7f11c07ea..8f1c3ba02 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmAbiTest.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmAbiTest.java @@ -22,6 +22,15 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsNull.nullValue; + @RunWith(JUnit4.class) public class KotlinBuilderJvmAbiTest { private static final KotlinJvmTestBuilder ctx = new KotlinJvmTestBuilder(); @@ -33,7 +42,6 @@ public void testGeneratesAbiOnly() { c.addSource("AClass.kt", "package something;" + "class AClass{}"); c.addSource("AnotherClass.kt", "package something;", "", "class AnotherClass{}"); c.outputJar(); - c.outputAbiJar(); c.compileKotlin(); c.outputJdeps(); }); @@ -60,4 +68,62 @@ public void testGeneratesAbiJarSource() { c.compileKotlin(); }); } + + @Test + public void testGeneratesPublicAbiOnly() throws IOException { + Deps.Dep d = ctx.runCompileTask( + c -> { + c.addSource("AClass.kt", "package something;" + "class AClass{}"); + c.addSource("AnotherClass.kt", "package something;", "", "class AnotherClass{}"); + c.addSource("NonPublicClass.kt", "package something;", "", "internal class NonPublicClass{}"); + c.outputJar(); + c.outputAbiJar(); + c.publicOnlyAbiJar(); + c.compileKotlin(); + c.outputJdeps(); + }); + + String abiJarPath = d.compileJars() + .stream() + .filter( (name)->name.endsWith("abi.jar") ) + .findFirst() + .orElse(null); + + assertThat(abiJarPath, is(not(nullValue()))); + + ZipEntry entry = null; + try( ZipFile zipFile = new ZipFile(abiJarPath) ) { + entry = zipFile.getEntry("something/NonPublicClass.class"); + } + assertThat(entry, is(nullValue())); + } + + + @Test + public void testGeneratesAbiIncludingInternal() throws IOException { + Deps.Dep d = ctx.runCompileTask( + c -> { + c.addSource("AClass.kt", "package something;" + "class AClass{}"); + c.addSource("AnotherClass.kt", "package something;", "", "class AnotherClass{}"); + c.addSource("NonPublicClass.kt", "package something;", "", "internal class NonPublicClass{}"); + c.outputJar(); + c.outputAbiJar(); + c.compileKotlin(); + c.outputJdeps(); + }); + + String abiJarPath = d.compileJars() + .stream() + .filter( (name)->name.endsWith("abi.jar") ) + .findFirst() + .orElse(null); + + assertThat(abiJarPath, is(not(nullValue()))); + + ZipEntry entry = null; + try( ZipFile zipFile = new ZipFile(abiJarPath) ) { + entry = zipFile.getEntry("something/NonPublicClass.class"); + } + assertThat(entry, is(not(nullValue()))); + } }