diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 7370f85a7..10947647f 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -18,7 +18,7 @@ tasks: - "//src/test/kotlin/io/bazel/kotlin/builder:builder_tests" # KotlinJvmDaggerExampleTest and KotlinJvmKaptAssertionTest are not remote # execution compatible, do not run them for now. - - "//src/test/kotlin/io/bazel/kotlin:KotlinJvmFriendsVisibilityTest" + - "//src/test/kotlin/io/bazel/kotlin:KotlinJvmAssociatesBasicVisibilityTest" - "//src/test/kotlin/io/bazel/kotlin:KotlinJvmBasicAssertionTest" test_flags: # Override the default worker strategy for remote builds (worker strategy @@ -37,6 +37,16 @@ tasks: - "--override_repository=io_bazel_rules_kotlin=/tmp/rules_kotlin_release" test_targets: - //... + example-associates: + name: "Example - Associates" + platform: ubuntu1804 + shell_commands: + - "cd ../.. && bazel build //:rules_kotlin_release && rm -rf /tmp/rules_kotlin_release && mkdir -p /tmp/rules_kotlin_release && tar -C /tmp/rules_kotlin_release -xvf bazel-bin/rules_kotlin_release.tgz" + working_directory: examples/associates + test_flags: + - "--override_repository=io_bazel_rules_kotlin=/tmp/rules_kotlin_release" + test_targets: + - //... example-anvil: name: "Example - Anvil" platform: ubuntu1804 diff --git a/.bazelignore b/.bazelignore index 26962a89d..6e5bc4a5d 100644 --- a/.bazelignore +++ b/.bazelignore @@ -2,6 +2,7 @@ # we don't break trying to build separate workspaces using wildcards like //... # examples/dagger doesn't have its own workspace, so don't do all of examples. examples/android +examples/associates examples/jetpack_compose examples/node examples/trivial diff --git a/README.md b/README.md index 4eb971771..a813994c7 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ and `kt_js`, and `kt_android` typically applied to the rules (the exception bein `kt_android_local_test`, which doesn't exist. Use an `android_local_test` that takes a `kt_android_library` as a dependency). -Limited "friend" support is available, in the form of tests being friends of their library for the -system under test, allowing `internal` access to types and functions. +Support for kotlin's -Xfriend-paths via the `associates=` attribute in the jvm allow access to +`internal` members. Also, `kt_jvm_*` rules support the following standard `java_*` rules attributes: * `data` diff --git a/examples/associates/.bazelversion b/examples/associates/.bazelversion new file mode 120000 index 000000000..96cf94962 --- /dev/null +++ b/examples/associates/.bazelversion @@ -0,0 +1 @@ +../../.bazelversion \ No newline at end of file diff --git a/examples/associates/WORKSPACE b/examples/associates/WORKSPACE new file mode 100644 index 000000000..307e04d4c --- /dev/null +++ b/examples/associates/WORKSPACE @@ -0,0 +1,44 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +local_repository( + name = "io_bazel_rules_kotlin", + path = "../..", +) + +load("@io_bazel_rules_kotlin//kotlin:dependencies.bzl", "kt_download_local_dev_dependencies") + +kt_download_local_dev_dependencies() + +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains") + +kotlin_repositories() + +kt_register_toolchains() + +RULES_JVM_EXTERNAL_TAG = "2.7" + +RULES_JVM_EXTERNAL_SHA = "f04b1466a00a2845106801e0c5cec96841f49ea4e7d1df88dc8e4bf31523df74" + +http_archive( + name = "rules_jvm_external", + sha256 = RULES_JVM_EXTERNAL_SHA, + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + artifacts = [ + "junit:junit:4.13", + ], + repositories = [ + "https://repo1.maven.org/maven2", + ], +) + +http_archive( + name = "rules_pkg", + sha256 = "4ba8f4ab0ff85f2484287ab06c0d871dcb31cc54d439457d28fd4ae14b18450a", + url = "https://github.com/bazelbuild/rules_pkg/releases/download/0.2.4/rules_pkg-0.2.4.tar.gz", +) diff --git a/examples/associates/projects/core/api/BUILD.bazel b/examples/associates/projects/core/api/BUILD.bazel new file mode 100644 index 000000000..54b5f68b2 --- /dev/null +++ b/examples/associates/projects/core/api/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") + +package(default_visibility = ["//visibility:public"]) + +kt_jvm_library( + name = "api", + srcs = glob(["src/main/kotlin/**/*.kt"]), + deps = [], +) diff --git a/examples/associates/projects/core/api/src/main/kotlin/core/api/api.kt b/examples/associates/projects/core/api/src/main/kotlin/core/api/api.kt new file mode 100644 index 000000000..aa5de6c96 --- /dev/null +++ b/examples/associates/projects/core/api/src/main/kotlin/core/api/api.kt @@ -0,0 +1,16 @@ +package core.api + +interface SomeInterface { + val name: String + val camelName: String +} + +data class MyType( + override val name: String +) : SomeInterface { + override val camelName: String = name.camelCase() +} + +internal fun String.camelCase() = this.split("_").joinToString("") { + "${it[0].toUpperCase()}${it.substring(1)}" +} diff --git a/examples/associates/projects/core/api/src/test/kotlin/core/api/BUILD.bazel b/examples/associates/projects/core/api/src/test/kotlin/core/api/BUILD.bazel new file mode 100644 index 000000000..970fc1fb8 --- /dev/null +++ b/examples/associates/projects/core/api/src/test/kotlin/core/api/BUILD.bazel @@ -0,0 +1,10 @@ +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_test") + +kt_jvm_test( + name = "CoreApiTest", + srcs = ["CoreApiTest.kt"], + friends = ["//projects/core/api"], # Deprecated - here to ensure it still works. + deps = [ + "@maven//:junit_junit", + ], +) diff --git a/examples/associates/projects/core/api/src/test/kotlin/core/api/CoreApiTest.kt b/examples/associates/projects/core/api/src/test/kotlin/core/api/CoreApiTest.kt new file mode 100644 index 000000000..f7874f586 --- /dev/null +++ b/examples/associates/projects/core/api/src/test/kotlin/core/api/CoreApiTest.kt @@ -0,0 +1,15 @@ +package core.api + +import org.junit.Assert.assertEquals +import org.junit.Test + +class CoreApiTest { + @Test fun testCamelCaseVar() { + val foo = MyType("foo_bar") + assertEquals("FooBar", foo.camelName) + } + + @Test fun testCamelCaseFun() { + assertEquals("FooBar", "foo_bar".camelCase()) + } +} diff --git a/examples/associates/projects/core/impl/BUILD.bazel b/examples/associates/projects/core/impl/BUILD.bazel new file mode 100644 index 000000000..c1055d289 --- /dev/null +++ b/examples/associates/projects/core/impl/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "impl", + srcs = glob(["src/main/kotlin/**/*.kt"]), + associates = ["//projects/core/api"], + visibility = [ + "//projects/core:__subpackages__", + ], + deps = [ + # All my deps are associates. + ], +) diff --git a/examples/associates/projects/core/impl/src/main/kotlin/core/impl/impl.kt b/examples/associates/projects/core/impl/src/main/kotlin/core/impl/impl.kt new file mode 100644 index 000000000..3ae75945d --- /dev/null +++ b/examples/associates/projects/core/impl/src/main/kotlin/core/impl/impl.kt @@ -0,0 +1,13 @@ +package core.impl + +import core.api.SomeInterface +import core.api.camelCase + +internal data class ImplType( + override val name: String +) : SomeInterface { + override val camelName: String = name.camelCase() + val customName = name.customStuff() +} + +internal fun String.customStuff() = "${hashCode()}" diff --git a/examples/associates/projects/core/impl/src/test/kotlin/core/impl/BUILD.bazel b/examples/associates/projects/core/impl/src/test/kotlin/core/impl/BUILD.bazel new file mode 100644 index 000000000..a0525d3f4 --- /dev/null +++ b/examples/associates/projects/core/impl/src/test/kotlin/core/impl/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_test") + +kt_jvm_test( + name = "CoreImplTest", + srcs = ["CoreImplTest.kt"], + associates = [ + "//projects/core/api", + "//projects/core/impl", + ], + deps = [ + "@maven//:junit_junit", + ], +) diff --git a/examples/associates/projects/core/impl/src/test/kotlin/core/impl/CoreImplTest.kt b/examples/associates/projects/core/impl/src/test/kotlin/core/impl/CoreImplTest.kt new file mode 100644 index 000000000..5cea5f049 --- /dev/null +++ b/examples/associates/projects/core/impl/src/test/kotlin/core/impl/CoreImplTest.kt @@ -0,0 +1,17 @@ +package core.impl + +import core.api.camelCase +import org.junit.Assert.assertEquals +import org.junit.Test + +class CoreImplTest { + @Test fun testCamelCaseVar() { + val foo = ImplType("foo_bar") + assertEquals("FooBar", foo.camelName) + } + + @Test fun testCamelCaseFun() { + // Testing transitivity here. TODO: Once strict deps are in place, delete this case. + assertEquals("FooBar", "foo_bar".camelCase()) + } +} diff --git a/kotlin/internal/defs.bzl b/kotlin/internal/defs.bzl index 06f49e5a8..8f5702bad 100644 --- a/kotlin/internal/defs.bzl +++ b/kotlin/internal/defs.bzl @@ -25,6 +25,7 @@ KT_COMPILER_REPO = "com_github_jetbrains_kotlin" KtJvmInfo = provider( fields = { "module_name": "the module name", + "module_jars": "Jars comprising the module (logical compilation unit), a.k.a. associates", "exported_compiler_plugins": "compiler plugins to be invoked by targets depending on this.", "srcs": "the source files. [intelij-aspect]", "outputs": "output jars produced by this rule. [intelij-aspect]", diff --git a/kotlin/internal/jvm/android.bzl b/kotlin/internal/jvm/android.bzl index b7dd9161c..59d9083d7 100644 --- a/kotlin/internal/jvm/android.bzl +++ b/kotlin/internal/jvm/android.bzl @@ -18,7 +18,17 @@ load( _ANDROID_SDK_JAR = "%s" % Label("//third_party:android_sdk") -def _kt_android_artifact(name, srcs = [], deps = [], plugins = [], friends = [], kotlinc_opts = None, javac_opts = None, enable_data_binding = False, **kwargs): +def _kt_android_artifact( + name, + srcs = [], + deps = [], + plugins = [], + friends = None, + associates = [], + kotlinc_opts = None, + javac_opts = None, + enable_data_binding = False, + **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. """ @@ -42,6 +52,7 @@ def _kt_android_artifact(name, srcs = [], deps = [], plugins = [], friends = [], deps = base_deps + [base_name], plugins = plugins, friends = friends, + associates = associates, testonly = kwargs.get("testonly", default = False), visibility = ["//visibility:private"], kotlinc_opts = kotlinc_opts, diff --git a/kotlin/internal/jvm/associates.bzl b/kotlin/internal/jvm/associates.bzl new file mode 100644 index 000000000..74661024a --- /dev/null +++ b/kotlin/internal/jvm/associates.bzl @@ -0,0 +1,83 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +load( + "//kotlin/internal:defs.bzl", + _KtJvmInfo = "KtJvmInfo", +) +load( + "//kotlin/internal/utils:sets.bzl", + _sets = "sets", +) +load( + "//kotlin/internal/utils:utils.bzl", + _utils = "utils", +) + +def _get_associates(ctx): + """Creates a struct of associates meta data""" + + friends_legacy = getattr(ctx.attr, "friends", []) + associates = getattr(ctx.attr, "associates", []) + + if friends_legacy: + print("WARNING: friends=[...] is deprecated, please prefer associates=[...] instead.") + if associates: + fail("friends= may not be used together with associates=. Use one or the other.") + elif ctx.attr.testonly == False: + fail("Only testonly targets can use the friends attribute. ") + else: + associates = friends_legacy + + if not bool(associates): + return struct( + targets = [], + module_name = _utils.derive_module_name(ctx), + jars = [], + ) + elif ctx.attr.module_name: + fail("if associates have been set then module_name cannot be provided") + else: + jars = [depset([a], transitive = a[_KtJvmInfo].module_jars) for a in associates] + module_names = _sets.copy_of([x[_KtJvmInfo].module_name for x in associates]) + if len(module_names) > 1: + fail("Dependencies from several different kotlin modules cannot be associated. " + + "Associates can see each other's \"internal\" members, and so must only be " + + "used with other targets in the same module: \n%s" % module_names) + if len(module_names) < 1: + # This should be impossible + fail("Error in rules - a KtJvmInfo was found which did not have a module_name") + return struct( + targets = associates, + jars = jars, + module_name = list(module_names)[0], + ) + +def _flatten_jars(nested_jars_depset): + """Returns a list of strings containing the compile_jars for depset of targets. + + This ends up unwinding the nesting of depsets, since compile_jars contains depsets inside + the nested_jars targets, which themselves are depsets. This function is intended to be called + lazily form within Args.add_all(map_each) as it collapses depsets. + """ + compile_jars_depsets = [ + target[JavaInfo].compile_jars + for target in nested_jars_depset.to_list() + if target[JavaInfo].compile_jars + ] + return [file.path for file in depset(transitive = compile_jars_depsets).to_list()] + +associate_utils = struct( + get_associates = _get_associates, + flatten_jars = _flatten_jars, +) diff --git a/kotlin/internal/jvm/compile.bzl b/kotlin/internal/jvm/compile.bzl index eb61cc028..0542014f9 100644 --- a/kotlin/internal/jvm/compile.bzl +++ b/kotlin/internal/jvm/compile.bzl @@ -34,10 +34,18 @@ load( _plugins_to_classpaths = "plugins_to_classpaths", _plugins_to_options = "plugins_to_options", ) +load( + "//kotlin/internal/jvm:associates.bzl", + _associate_utils = "associate_utils", +) load( "//kotlin/internal/utils:utils.bzl", _utils = "utils", ) +load( + "//kotlin/internal/utils:sets.bzl", + _sets = "sets", +) load( "@bazel_tools//tools/jdk:toolchain_utils.bzl", "find_java_runtime_toolchain", @@ -87,36 +95,18 @@ def _compiler_toolchains(ctx): java_runtime = find_java_runtime_toolchain(ctx, ctx.attr._host_javabase), ) -def _compiler_friends(ctx, friends): - """Creates a struct of friends meta data""" - - if len(friends) > 0 and ctx.attr.testonly == False: - fail("only testonly targets can have friends associated with them") - - # TODO extract and move this into common. Need to make it generic first. - if len(friends) == 0: - return struct( - targets = [], - module_name = _utils.derive_module_name(ctx), - paths = [], - ) - elif len(friends) == 1: - if friends[0][_KtJvmInfo] == None: - fail("only kotlin dependencies can be friends") - elif ctx.attr.module_name: - fail("if friends has been set then module_name cannot be provided") - else: - return struct( - targets = friends, - paths = _java_info(friends[0]).compile_jars, - module_name = friends[0][_KtJvmInfo].module_name, - ) - else: - fail("only one friend is possible") - -def _jvm_deps(toolchains, friend, deps, runtime_deps = []): +def _jvm_deps(toolchains, associated_targets, deps, runtime_deps = []): """Encapsulates jvm dependency metadata.""" - dep_infos = [_java_info(d) for d in friend.targets + deps] + [toolchains.kt.jvm_stdlibs] + diff = _sets.intersection( + _sets.copy_of([x.label for x in associated_targets]), + _sets.copy_of([x.label for x in deps]), + ) + if diff: + fail( + "\n------\nTargets should only be put in associates= or deps=, not both:\n%s" % + ",\n ".join([" %s" % x for x in list(diff)]), + ) + dep_infos = [_java_info(d) for d in associated_targets + deps] + [toolchains.kt.jvm_stdlibs] return struct( deps = dep_infos, compile_jars = depset( @@ -179,13 +169,14 @@ def _adjust_resources_path(path, resource_strip_prefix): else: return _adjust_resources_path_by_default_prefixes(path) +# TODO: Figure this out and delete if really unused. def _merge_kt_jvm_info(module_name, providers): language_versions = {p.language_version: True for p in providers if p.language_version} if len(language_versions) != 1: fail("Conflicting kt language versions: %s" % language_versions) return _KtJvmInfo( language_versions.keys()[0], - modules_jar = [p.module_jars for p in providers], + module_jars = [p.module_jars for p in providers], exported_compiler_plugins = depset(transitive = [ p.exported_compiler_plugins for p in providers @@ -367,7 +358,7 @@ def _run_kt_builder_action( toolchains, srcs, generated_src_jars, - friend, + associates, compile_deps, deps_artifacts, annotation_processors, @@ -378,7 +369,7 @@ def _run_kt_builder_action( build_kotlin = True, mnemonic = "KotlinCompile"): """Creates a KotlinBuilder action invocation.""" - args = _utils.init_args(ctx, rule_kind, friend.module_name) + args = _utils.init_args(ctx, rule_kind, associates.module_name) for f, path in outputs.items(): args.add("--" + f, path) @@ -398,8 +389,7 @@ def _run_kt_builder_action( args.add_all("--sources", srcs.all_srcs, omit_if_empty = True) args.add_all("--source_jars", srcs.src_jars + generated_src_jars, omit_if_empty = True) args.add_all("--deps_artifacts", deps_artifacts, omit_if_empty = True) - - args.add_joined("--kotlin_friend_paths", friend.paths, join_with = "\n") + args.add_all("--kotlin_friend_paths", associates.jars, map_each = _associate_utils.flatten_jars) # Collect and prepare plugin descriptor for the worker. args.add_all( @@ -526,17 +516,17 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): """ toolchains = _compiler_toolchains(ctx) srcs = _partitioned_srcs(ctx.files.srcs) - friend = _compiler_friends(ctx, friends = getattr(ctx.attr, "friends", [])) + associates = _associate_utils.get_associates(ctx) compile_deps = _jvm_deps( toolchains, - friend, + associates.targets, deps = ctx.attr.deps, runtime_deps = ctx.attr.runtime_deps, ) annotation_processors = _plugin_mappers.targets_to_annotation_processors(ctx.attr.plugins + ctx.attr.deps) transitive_runtime_jars = _plugin_mappers.targets_to_transitive_runtime_jars(ctx.attr.plugins + ctx.attr.deps) plugins = ctx.attr.plugins + _exported_plugins(deps = ctx.attr.deps) - deps_artifacts = _deps_artifacts(toolchains, ctx.attr.deps + friend.targets) + deps_artifacts = _deps_artifacts(toolchains, ctx.attr.deps + associates.targets) generated_src_jars = [] annotation_processing = None @@ -548,7 +538,7 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): toolchains = toolchains, srcs = srcs, generated_src_jars = [], - friend = friend, + associates = associates, compile_deps = compile_deps, deps_artifacts = deps_artifacts, annotation_processors = annotation_processors, @@ -572,7 +562,7 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): toolchains = toolchains, srcs = srcs, generated_src_jars = [], - friend = friend, + associates = associates, compile_deps = compile_deps, deps_artifacts = deps_artifacts, annotation_processors = annotation_processors, @@ -643,7 +633,8 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): java = java_info, kt = _KtJvmInfo( srcs = ctx.files.srcs, - module_name = _utils.derive_module_name(ctx), + module_name = associates.module_name, + module_jars = associates.jars, language_version = toolchains.kt.api_version, exported_compiler_plugins = _collect_plugins_for_export( getattr(ctx.attr, "exported_compiler_plugins", []), @@ -665,7 +656,19 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): ) -def _run_kt_java_builder_actions(ctx, rule_kind, toolchains, srcs, generated_src_jars, friend, compile_deps, deps_artifacts, annotation_processors, transitive_runtime_jars, plugins, compile_jar): +def _run_kt_java_builder_actions( + ctx, + rule_kind, + toolchains, + srcs, + generated_src_jars, + associates, + compile_deps, + deps_artifacts, + annotation_processors, + transitive_runtime_jars, + plugins, + compile_jar): """Runs the necessary KotlinBuilder and JavaBuilder actions to compile a jar Returns: @@ -686,7 +689,7 @@ def _run_kt_java_builder_actions(ctx, rule_kind, toolchains, srcs, generated_src toolchains = toolchains, srcs = srcs, generated_src_jars = [], - friend = friend, + associates = associates, compile_deps = compile_deps, deps_artifacts = deps_artifacts, annotation_processors = annotation_processors, @@ -737,7 +740,7 @@ def _run_kt_java_builder_actions(ctx, rule_kind, toolchains, srcs, generated_src toolchains = toolchains, srcs = srcs, generated_src_jars = generated_src_jars, - friend = friend, + associates = associates, compile_deps = compile_deps, deps_artifacts = deps_artifacts, annotation_processors = [], @@ -894,6 +897,7 @@ def export_only_providers(ctx, actions, attr, outputs): java = java, kt = _KtJvmInfo( module_name = _utils.derive_module_name(ctx), + module_jars = [], language_version = toolchains.kt.api_version, exported_compiler_plugins = _collect_plugins_for_export( getattr(attr, "exported_compiler_plugins", []), diff --git a/kotlin/internal/jvm/impl.bzl b/kotlin/internal/jvm/impl.bzl index cad524c2e..df3c33a5a 100644 --- a/kotlin/internal/jvm/impl.bzl +++ b/kotlin/internal/jvm/impl.bzl @@ -123,6 +123,7 @@ def kt_jvm_import_impl(ctx): artifact = _unify_jars(ctx) kt_info = _KtJvmInfo( module_name = _utils.derive_module_name(ctx), + module_jars = [], exported_compiler_plugins = depset(getattr(ctx.attr, "exported_compiler_plugins", [])), outputs = struct( jars = [artifact], @@ -159,7 +160,6 @@ def kt_jvm_library_impl(ctx): "\nTo export libraries use exports.", attr = "deps", ) - return _make_providers( ctx, _kt_jvm_produce_jar_actions(ctx, "kt_jvm_library") if ctx.attr.srcs else export_only_providers( diff --git a/kotlin/internal/jvm/jvm.bzl b/kotlin/internal/jvm/jvm.bzl index 6e8888f64..d8f169698 100644 --- a/kotlin/internal/jvm/jvm.bzl +++ b/kotlin/internal/jvm/jvm.bzl @@ -198,9 +198,17 @@ _common_attr = utils.add_dicts( [Attributes common to all build rules](https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes).""", allow_files = True, ), + "associates": attr.label_list( + doc = """Kotlin deps who should be considered part of the same module/compilation-unit + for the purposes of "internal" access. Such deps must all share the same module space + and so a target cannot associate to two deps from two different modules.""", + default = [], + providers = [JavaInfo, _KtJvmInfo], + ), "friends": attr.label_list( - doc = """A single Kotlin dep which allows Kotlin code in other modules access to internal members. Currently uses the output - jar of the module -- i.e., exported deps won't be included.""", + doc = """A single Kotlin dep which allows Kotlin code in other modules access to + internal members. Currently uses the output jar of the module -- i.e., exported + deps won't be included. [DEPRECATED, use "associates" instead]""", default = [], providers = [JavaInfo, _KtJvmInfo], ), diff --git a/kotlin/internal/utils/sets.bzl b/kotlin/internal/utils/sets.bzl new file mode 100644 index 000000000..78de26963 --- /dev/null +++ b/kotlin/internal/utils/sets.bzl @@ -0,0 +1,79 @@ +# +# Copyright 2018 The Bazel Authors. All rights reserved. +# Copyright 2018 Square, Inc. 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. +# +# Originally written for bazel_maven_repository, inspired by skylib +# +""" +A set of utilities that provide set-like behavior, using a dict (specifically its keys) as the underlying +implementation. Generally only use dictionaries created by sets.new() because values are normalized. Using +dictionaries from other sources may result in equality failing, and other odd behavior. +""" + +_UNDEFINED = "__UNDEFINED__" +_EMPTY = "__EMPTY__" # Check when changing this to keep in sync with sets.bzl +_SET_DICTIONARY_KEY = "_____SET_DICTIONARY_KEY______" + +def _contains(set, item): + """Returns true if the set contains the supplied item""" + return not (set.get(item, _UNDEFINED) == _UNDEFINED) + +def _add(set, item): + """Adds an item to the set and returns the set""" + set[item] = _EMPTY + return set + +def _add_all_as_list(set, items): + "Implementation for the add_* family of functions." + for item in items: + sets.add(set, item) + return set + +def _add_all(set, items): + """Adds all items in the list or all keys in the dictionary to the set and returns the set""" + item_type = type(items) + if item_type == type({}): + _add_all_as_list(set, list(items)) + elif item_type == type([]): + _add_all_as_list(set, items) + else: + fail("Error, invalid %s argument passed to set operation." % item_type) + return set + +def _new(*items): + """Creates a new set, from a variable array of parameters. """ + return {} if not bool(items) else sets.add_all({}, list(items)) + +def _copy_of(items): + """Creates a new set from a given list. """ + return {} if not bool(items) else sets.add_all({}, list(items)) + +def _difference(a, b): + """Returns the elements that reflect the set difference (items in a that are not in b)""" + return sets.copy_of([x for x in list(a) if not sets.contains(b, x)]) + +def _intersection(a, b): + """Returns the elements that exist in both A and B""" + return sets.difference(a, sets.difference(a, b)) + +sets = struct( + difference = _difference, + intersection = _intersection, + contains = _contains, + add = _add, + add_all = _add_all, + new = _new, + copy_of = _copy_of, +) diff --git a/src/test/data/jvm/basic/BUILD b/src/test/data/jvm/basic/BUILD index 74bee8d40..927aa50d4 100644 --- a/src/test/data/jvm/basic/BUILD +++ b/src/test/data/jvm/basic/BUILD @@ -104,7 +104,7 @@ java_binary( ) kt_jvm_library( - name = "test_friends_library", + name = "test_associates_library", srcs = ["test_friends/Service.kt"], visibility = ["//src/test/kotlin:__subpackages__"], ) diff --git a/src/test/kotlin/io/bazel/kotlin/BUILD b/src/test/kotlin/io/bazel/kotlin/BUILD index b291c8f8c..ca9d83544 100644 --- a/src/test/kotlin/io/bazel/kotlin/BUILD +++ b/src/test/kotlin/io/bazel/kotlin/BUILD @@ -58,9 +58,9 @@ kt_rules_e2e_test( ) kt_rules_e2e_test( - name = "KotlinJvmFriendsVisibilityTest", - srcs = ["KotlinJvmFriendsVisibilityTest.kt"], - friends = ["//src/test/data/jvm/basic:test_friends_library"], + name = "KotlinJvmAssociatesBasicVisibilityTest", + srcs = ["KotlinJvmAssociatesBasicVisibilityTest.kt"], + associates = ["//src/test/data/jvm/basic:test_associates_library"], ) kt_rules_e2e_test( @@ -72,9 +72,9 @@ test_suite( name = "assertion_tests", tests = [ "KotlinJvm13Test", + "KotlinJvmAssociatesBasicVisibilityTest", "KotlinJvmBasicAssertionTest", "KotlinJvmDaggerExampleTest", - "KotlinJvmFriendsVisibilityTest", "KotlinJvmKaptAssertionTest", ], ) diff --git a/src/test/kotlin/io/bazel/kotlin/KotlinJvmFriendsVisibilityTest.kt b/src/test/kotlin/io/bazel/kotlin/KotlinJvmAssociatesBasicVisibilityTest.kt similarity index 58% rename from src/test/kotlin/io/bazel/kotlin/KotlinJvmFriendsVisibilityTest.kt rename to src/test/kotlin/io/bazel/kotlin/KotlinJvmAssociatesBasicVisibilityTest.kt index 32d71a936..84edcc504 100644 --- a/src/test/kotlin/io/bazel/kotlin/KotlinJvmFriendsVisibilityTest.kt +++ b/src/test/kotlin/io/bazel/kotlin/KotlinJvmAssociatesBasicVisibilityTest.kt @@ -4,10 +4,10 @@ import test.DEFAULT_FRIEND import test.Service /** - * This test validates that friend visibility is working. Services and DEFAULT_FRIEND are internal another compilation - * unit. + * This test validates that internal visibility is working. Services and DEFAULT_FRIEND are + * internal another compilation unit. */ -class KotlinJvmFriendsVisibilityTest { +class KotlinJvmAssociatesBasicVisibilityTest { val service: Service = Service() @org.junit.Test