From 97b008aefc006ab3b84b5022bb89c78236e0652b Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Sat, 22 Oct 2022 08:35:11 -0700 Subject: [PATCH 1/4] [WIP] Starlark implementation of kt_android_* rules --- .bazelrc | 1 + examples/android/.bazelrc | 1 + examples/android/WORKSPACE | 16 +- examples/android/app/BUILD.bazel | 2 +- examples/android/libAndroid/BUILD.bazel | 2 +- examples/android/libKtAndroid/BUILD.bazel | 8 - .../libKtAndroid/res/values/strings.xml | 1 + .../libKtAndroid/res2/values/strings.xml | 3 - .../java/examples/android/lib/BUILD.bazel | 2 +- examples/anvil/.bazelrc | 1 + examples/anvil/WORKSPACE | 39 ++- examples/anvil/app/BUILD.bazel | 2 +- examples/associates/.bazelrc | 1 + examples/associates/WORKSPACE | 23 +- examples/jetpack_compose/.bazelrc | 3 + examples/jetpack_compose/BUILD | 9 +- examples/jetpack_compose/WORKSPACE | 18 +- .../jetpack_compose/app/AndroidManifest.xml | 9 +- examples/jetpack_compose/app/BUILD | 2 +- examples/plugin/.bazelrc | 1 + examples/plugin/WORKSPACE | 25 +- kotlin/internal/jvm/BUILD | 2 +- kotlin/internal/jvm/android.bzl | 266 ++++++++------ kotlin/internal/jvm/android_impl.bzl | 327 ++++++++++++++++++ kotlin/internal/jvm/android_resources.bzl | 79 +++++ kotlin/internal/jvm/compile.bzl | 111 ++++-- kotlin/internal/jvm/external_java_utils.bzl | 110 ++++++ kotlin/internal/jvm/impl.bzl | 3 + kotlin/internal/jvm/jvm.bzl | 7 + kotlin/internal/jvm/utils.bzl | 68 ++++ .../starlark/core/repositories/download.bzl | 5 +- src/main/starlark/core/repositories/setup.bzl | 2 +- .../starlark/core/repositories/versions.bzl | 10 +- 33 files changed, 973 insertions(+), 186 deletions(-) create mode 100644 examples/android/.bazelrc delete mode 100644 examples/android/libKtAndroid/res2/values/strings.xml create mode 100644 examples/anvil/.bazelrc create mode 100644 examples/associates/.bazelrc create mode 100644 examples/plugin/.bazelrc create mode 100644 kotlin/internal/jvm/android_impl.bzl create mode 100644 kotlin/internal/jvm/android_resources.bzl create mode 100644 kotlin/internal/jvm/external_java_utils.bzl create mode 100644 kotlin/internal/jvm/utils.bzl diff --git a/.bazelrc b/.bazelrc index 6858b6449..84ebabe9e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,4 +1,5 @@ build --strategy=KotlinCompile=worker build --test_output=all build --verbose_failures +build --experimental_google_legacy_api diff --git a/examples/android/.bazelrc b/examples/android/.bazelrc new file mode 100644 index 000000000..d5b238e6c --- /dev/null +++ b/examples/android/.bazelrc @@ -0,0 +1 @@ +build --experimental_google_legacy_api diff --git a/examples/android/WORKSPACE b/examples/android/WORKSPACE index cbf37f536..dd743a3c8 100644 --- a/examples/android/WORKSPACE +++ b/examples/android/WORKSPACE @@ -56,18 +56,24 @@ maven_install( "https://maven.google.com", "https://repo1.maven.org/maven2", ], + use_starlark_android_rules = True, + aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", ) http_archive( - name = "build_bazel_rules_android", + name = "io_bazel_rules_android", sha256 = versions.ANDROID.SHA, strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, - urls = ["https://github.com/bazelbuild/rules_android/archive/v%s.zip" % versions.ANDROID.VERSION], + urls = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], ) -load( - "@build_bazel_rules_android//android:rules.bzl", - "android_sdk_repository", +load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") + +rules_android_workspace() + +register_toolchains( + "@io_bazel_rules_android//toolchains/android:android_default_toolchain", + "@io_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", ) android_sdk_repository( diff --git a/examples/android/app/BUILD.bazel b/examples/android/app/BUILD.bazel index 2bd8b3035..d8c7b83c2 100644 --- a/examples/android/app/BUILD.bazel +++ b/examples/android/app/BUILD.bazel @@ -1,5 +1,5 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@build_bazel_rules_android//android:rules.bzl", "android_binary") +load("@io_bazel_rules_android//rules:rules.bzl", "android_binary") # An app that consumes android-kt deps android_binary( diff --git a/examples/android/libAndroid/BUILD.bazel b/examples/android/libAndroid/BUILD.bazel index 3369b3289..0fd9b3772 100644 --- a/examples/android/libAndroid/BUILD.bazel +++ b/examples/android/libAndroid/BUILD.bazel @@ -1,4 +1,4 @@ -load("@build_bazel_rules_android//android:rules.bzl", "android_library") +load("@io_bazel_rules_android//rules:rules.bzl", "android_library") load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") android_library( diff --git a/examples/android/libKtAndroid/BUILD.bazel b/examples/android/libKtAndroid/BUILD.bazel index ddb46c373..6a4205020 100644 --- a/examples/android/libKtAndroid/BUILD.bazel +++ b/examples/android/libKtAndroid/BUILD.bazel @@ -32,16 +32,8 @@ kt_android_library( tags = ["trace"], visibility = ["//visibility:public"], deps = [ - ":res2", "@maven//:androidx_appcompat_appcompat", "@maven//:com_google_auto_value_auto_value_annotations", "@maven//:org_jetbrains_kotlinx_kotlinx_serialization_runtime", ], ) - -android_library( - name = "res2", - custom_package = "examples.android.lib", - manifest = "src/main/AndroidManifest.xml", - resource_files = glob(["res2/**"]), -) diff --git a/examples/android/libKtAndroid/res/values/strings.xml b/examples/android/libKtAndroid/res/values/strings.xml index 4438981a8..9db78c344 100644 --- a/examples/android/libKtAndroid/res/values/strings.xml +++ b/examples/android/libKtAndroid/res/values/strings.xml @@ -1,3 +1,4 @@ Where you at? + hello? diff --git a/examples/android/libKtAndroid/res2/values/strings.xml b/examples/android/libKtAndroid/res2/values/strings.xml deleted file mode 100644 index d6592431f..000000000 --- a/examples/android/libKtAndroid/res2/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - hello? - diff --git a/examples/android/libKtAndroid/src/test/java/examples/android/lib/BUILD.bazel b/examples/android/libKtAndroid/src/test/java/examples/android/lib/BUILD.bazel index 901dcc60b..680a44e18 100644 --- a/examples/android/libKtAndroid/src/test/java/examples/android/lib/BUILD.bazel +++ b/examples/android/libKtAndroid/src/test/java/examples/android/lib/BUILD.bazel @@ -3,7 +3,7 @@ load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_local_test") kt_android_local_test( name = "SomeTest", srcs = ["SomeTest.kt"], - associates = ["//libKtAndroid:my_kt_kt"], + associates = ["//libKtAndroid:my_kt"], custom_package = "examples.android.lib", manifest = "AndroidManifest.xml", deps = [ diff --git a/examples/anvil/.bazelrc b/examples/anvil/.bazelrc new file mode 100644 index 000000000..44a921772 --- /dev/null +++ b/examples/anvil/.bazelrc @@ -0,0 +1 @@ +common --experimental_google_legacy_api diff --git a/examples/anvil/WORKSPACE b/examples/anvil/WORKSPACE index 186e2d531..65feef4e0 100644 --- a/examples/anvil/WORKSPACE +++ b/examples/anvil/WORKSPACE @@ -17,23 +17,6 @@ register_toolchains("//:kotlin_toolchain") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "build_bazel_rules_android", - sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", - strip_prefix = "rules_android-0.1.1", - urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"], -) - -load( - "@build_bazel_rules_android//android:rules.bzl", - "android_sdk_repository", -) - -android_sdk_repository( - name = "androidsdk", - build_tools_version = versions.ANDROID.BUILD_TOOLS, -) - # Skylib, for build_test, so don't bother initializing the unit test infrastructure. http_archive( name = "bazel_skylib", @@ -115,4 +98,26 @@ maven_install( "https://maven.google.com", "https://repo1.maven.org/maven2", ], + use_starlark_android_rules = True, + aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", +) + +http_archive( + name = "io_bazel_rules_android", + sha256 = versions.ANDROID.SHA, + strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, + urls = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], +) + +load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") + +rules_android_workspace() + +register_toolchains( + "@io_bazel_rules_android//toolchains/android:android_default_toolchain", + "@io_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", +) +android_sdk_repository( + name = "androidsdk", + build_tools_version = versions.ANDROID.BUILD_TOOLS, ) diff --git a/examples/anvil/app/BUILD.bazel b/examples/anvil/app/BUILD.bazel index 7b8a5a2b7..82e318cb9 100644 --- a/examples/anvil/app/BUILD.bazel +++ b/examples/anvil/app/BUILD.bazel @@ -1,4 +1,4 @@ -load("@build_bazel_rules_android//android:rules.bzl", "android_binary") +load("@io_bazel_rules_android//rules:rules.bzl", "android_binary") android_binary( name = "app", diff --git a/examples/associates/.bazelrc b/examples/associates/.bazelrc new file mode 100644 index 000000000..44a921772 --- /dev/null +++ b/examples/associates/.bazelrc @@ -0,0 +1 @@ +common --experimental_google_legacy_api diff --git a/examples/associates/WORKSPACE b/examples/associates/WORKSPACE index 4fa21580d..92e77aa5e 100644 --- a/examples/associates/WORKSPACE +++ b/examples/associates/WORKSPACE @@ -19,13 +19,6 @@ kt_register_toolchains() load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "build_bazel_rules_android", - sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", - strip_prefix = "rules_android-0.1.1", - urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"], -) - http_archive( name = "bazel_skylib", sha256 = versions.SKYLIB_SHA, @@ -53,6 +46,22 @@ maven_install( ], ) +http_archive( + name = "io_bazel_rules_android", + sha256 = versions.ANDROID.SHA, + strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, + urls = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], +) + +load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") + +rules_android_workspace() + +register_toolchains( + "@io_bazel_rules_android//toolchains/android:android_default_toolchain", + "@io_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", +) + http_archive( name = "rules_pkg", sha256 = "4ba8f4ab0ff85f2484287ab06c0d871dcb31cc54d439457d28fd4ae14b18450a", diff --git a/examples/jetpack_compose/.bazelrc b/examples/jetpack_compose/.bazelrc index e11a6c594..fc47b9241 100644 --- a/examples/jetpack_compose/.bazelrc +++ b/examples/jetpack_compose/.bazelrc @@ -5,3 +5,6 @@ build --define=android_dexmerger_tool=d8_dexmerger build --define=android_incremental_dexing_tool=d8_dexbuilder build --define=android_standalone_dexing_tool=d8_compat_dx build --nouse_workers_with_dexbuilder + +common --experimental_google_legacy_api + diff --git a/examples/jetpack_compose/BUILD b/examples/jetpack_compose/BUILD index 6b7949c10..a8d80c91d 100644 --- a/examples/jetpack_compose/BUILD +++ b/examples/jetpack_compose/BUILD @@ -33,11 +33,16 @@ kt_compiler_plugin( # Used in 'override_targets' by referencing @//:kotlinx_coroutines_core_jvm kt_jvm_import( name = "kotlinx_coroutines_core_jvm", - jars = ["@maven_secondary//:v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.3/kotlinx-coroutines-core-jvm-1.6.3.jar"], - srcjar = "@maven_secondary//:v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.3/kotlinx-coroutines-core-jvm-1.6.3-sources.jar", + jars = [":kotlinx_coroutines_core_jvm_jar"], visibility = ["//visibility:public"], deps = [ "//stub:sun_misc", "@maven//:org_jetbrains_kotlin_kotlin_stdlib", ], ) + +# HACK: for some reason the underlying jar is not visible when using starlark rules +filegroup( + name = "kotlinx_coroutines_core_jvm_jar", + srcs = ["@maven_secondary//:org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_1_6_3"], +) diff --git a/examples/jetpack_compose/WORKSPACE b/examples/jetpack_compose/WORKSPACE index 903531aa9..f4dac89aa 100644 --- a/examples/jetpack_compose/WORKSPACE +++ b/examples/jetpack_compose/WORKSPACE @@ -1,4 +1,5 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") _COMPOSE_VERSION = "1.2.1" @@ -62,6 +63,8 @@ maven_install( "https://maven.google.com", "https://repo1.maven.org/maven2", ], + use_starlark_android_rules = True, + aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", ) # Secondary maven repository used mainly for workarounds @@ -74,6 +77,8 @@ maven_install( ], fetch_sources = True, repositories = ["https://repo1.maven.org/maven2"], + use_starlark_android_rules = True, + aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", ) http_archive( @@ -88,13 +93,20 @@ http_archive( ## Android http_archive( - name = "build_bazel_rules_android", + name = "io_bazel_rules_android", sha256 = versions.ANDROID.SHA, strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, - urls = ["https://github.com/bazelbuild/rules_android/archive/v%s.zip" % versions.ANDROID.VERSION], + urls = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], ) -load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository") +load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") + +rules_android_workspace() + +register_toolchains( + "@io_bazel_rules_android//toolchains/android:android_default_toolchain", + "@io_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", +) android_sdk_repository( name = "androidsdk", diff --git a/examples/jetpack_compose/app/AndroidManifest.xml b/examples/jetpack_compose/app/AndroidManifest.xml index f3eb21691..200e05308 100644 --- a/examples/jetpack_compose/app/AndroidManifest.xml +++ b/examples/jetpack_compose/app/AndroidManifest.xml @@ -1,6 +1,13 @@ - \ No newline at end of file + + + + diff --git a/examples/jetpack_compose/app/BUILD b/examples/jetpack_compose/app/BUILD index d629988ba..17bb6196f 100644 --- a/examples/jetpack_compose/app/BUILD +++ b/examples/jetpack_compose/app/BUILD @@ -1,5 +1,5 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@build_bazel_rules_android//android:rules.bzl", "android_binary") +load("@io_bazel_rules_android//rules:rules.bzl", "android_binary") # An app that consumes android-kt deps android_binary( diff --git a/examples/plugin/.bazelrc b/examples/plugin/.bazelrc new file mode 100644 index 000000000..44a921772 --- /dev/null +++ b/examples/plugin/.bazelrc @@ -0,0 +1 @@ +common --experimental_google_legacy_api diff --git a/examples/plugin/WORKSPACE b/examples/plugin/WORKSPACE index ea9581461..dccddafe7 100644 --- a/examples/plugin/WORKSPACE +++ b/examples/plugin/WORKSPACE @@ -1,3 +1,5 @@ +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + local_repository( name = "release_archive", path = "../../src/main/starlark/release_archive", @@ -36,9 +38,30 @@ maven_install( repositories = [ "https://repo1.maven.org/maven2", ], + use_starlark_android_rules = True, + aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", +) + +http_archive( + name = "io_bazel_rules_android", + sha256 = versions.ANDROID.SHA, + strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, + urls = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], +) + +load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") + +rules_android_workspace() + +#register_toolchains("//bzl/android:android_default_toolchain") + +# Setup a custom toolchain from //bzl/kotlin:kotlin_toolchain +register_toolchains( + "@io_bazel_rules_android//toolchains/android:android_default_toolchain", + "@io_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", ) -load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository") + android_sdk_repository( name = "androidsdk", diff --git a/kotlin/internal/jvm/BUILD b/kotlin/internal/jvm/BUILD index bbae8718a..559143c5a 100644 --- a/kotlin/internal/jvm/BUILD +++ b/kotlin/internal/jvm/BUILD @@ -32,7 +32,7 @@ bzl_library( deps = [ "//third_party:bzl", "@bazel_skylib//rules:common_settings", - "@build_bazel_rules_android//android", + "@build_bazel_rules_android//rules", ], ) diff --git a/kotlin/internal/jvm/android.bzl b/kotlin/internal/jvm/android.bzl index d65f5a84f..ad9ad3b99 100644 --- a/kotlin/internal/jvm/android.bzl +++ b/kotlin/internal/jvm/android.bzl @@ -11,118 +11,176 @@ # 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", + _JAVA_RUNTIME_TOOLCHAIN_TYPE = "JAVA_RUNTIME_TOOLCHAIN_TYPE", + _JAVA_TOOLCHAIN_TYPE = "JAVA_TOOLCHAIN_TYPE", + _KtJvmInfo = "KtJvmInfo", + _TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE", +) load( "//kotlin/internal/jvm:jvm.bzl", _kt_jvm_library = "kt_jvm_library", + _kt_lib_common_attr = "lib_common_attr_exposed", + _kt_lib_common_outputs_exposed = "common_outputs_exposed", + _kt_android_local_test_runnable_common_attr_exposed = "runnable_common_attr_exposed" ) +load( + "//kotlin/internal/jvm:android_impl.bzl", + _kt_android_library_impl = "kt_android_library_impl", + _kt_android_local_test_impl = "kt_android_local_test_impl", +) +load("//kotlin/internal/utils:utils.bzl", "utils") -_ANDROID_SDK_JAR = "%s" % Label("//third_party:android_sdk") - -def _kt_android_artifact( - name, - srcs = [], - deps = [], - resources = [], - plugins = [], - associates = [], - kotlinc_opts = None, - javac_opts = None, - enable_data_binding = False, - tags = [], - exec_properties = 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. - """ - base_name = name + "_base" - kt_name = name + "_kt" - - # TODO(bazelbuild/rules_kotlin/issues/273): This should be retrieved from a provider. - base_deps = [_ANDROID_SDK_JAR] + deps - - # TODO(bazelbuild/rules_kotlin/issues/556): replace with starlark - # buildifier: disable=native-android - native.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 - ) - _kt_jvm_library( - name = kt_name, - srcs = srcs, - deps = [base_name] + base_deps, - resources = resources, - plugins = plugins, - associates = associates, - testonly = kwargs.get("testonly", default = False), - visibility = ["//visibility:public"], - kotlinc_opts = kotlinc_opts, - javac_opts = javac_opts, - tags = tags, - exec_properties = exec_properties, - ) - return [base_name, kt_name] +_common_toolchains = [ + "@io_bazel_rules_android//toolchains/android:toolchain_type", + _TOOLCHAIN_TYPE, + _JAVA_TOOLCHAIN_TYPE, + _JAVA_RUNTIME_TOOLCHAIN_TYPE, +] -def kt_android_library(name, exports = [], visibility = None, exec_properties = None, **kwargs): - """Creates an Android sandwich library. - `srcs`, `deps`, `plugins` are routed to `kt_jvm_library` the other android - related attributes are handled by the native `android_library` rule. - """ +kt_android_library = rule( + doc = """This rule compiles and links Kotlin and Java sources into a .jar file.""", + attrs = dict(_kt_lib_common_attr.items() + { + "manifest": attr.label( + doc = """The name of the Android manifest file, normally `AndroidManifest.xml`. + Must be defined if resource_files or assets are defined.""", + default = None, + allow_single_file = True, + ), + "exports_manifest": attr.int( + doc = """Whether to export manifest entries to `android_binary` targets + that depend on this target. `uses-permissions` attributes are never exported.""", + default = 1, + ), + "custom_package": attr.string( + doc = """Java package for which java sources will be generated. By default the package + is inferred from the directory where the BUILD file containing the rule is. You can + specify a different package but this is highly discouraged since it can introduce + classpath conflicts with other libraries that will only be detected at runtime.""", + ), + "resource_files": attr.label_list( + doc = """The list of resources to be packaged. This is typically a glob of all files + under the res directory. Generated files (from genrules) can be referenced by Label + here as well. The only restriction is that the generated outputs must be under the + same "res" directory as any other resource files that are included.""", + default = [], + allow_files = True, + ), + "assets": attr.label_list( + doc = """The list of assets to be packaged. This is typically a `glob` of all files + under the `assets` directory. You can also reference other rules (any rule that + produces files) or exported files in the other packages, as long as all those files + are under the `assets_dir` directory in the corresponding package.""", + default = [], + allow_files = True, + ), + "assets_dir": attr.string( + doc = """The string giving the path to the files in assets. The pair assets and + `assets_dir` describe packaged assets and either both attributes should be provided + or none of them.""", + ), + "friends": attr.label_list( + doc = """A single Kotlin dep which allows the test code access to internal members. Currently uses the output + jar of the module -- i.e., exported deps won't be included.""", + default = [], + providers = [JavaInfo], + ), + "_android_resources_busybox": attr.label( + executable = True, + cfg = "host", + default = Label("@bazel_tools//tools/android:busybox"), + allow_files = True, + ), + "android_sdk": attr.label( + default = Label("@bazel_tools//tools/android:sdk"), + allow_files = True, + ), + "proguard_specs": attr.label_list( + doc = """Files to be used as Proguard specification. - # TODO(bazelbuild/rules_kotlin/issues/556): replace with starlark - # buildifier: disable=native-android - native.android_library( - name = name, - exports = exports + _kt_android_artifact(name, exec_properties = exec_properties, **kwargs), - visibility = visibility, - tags = kwargs.get("tags", default = None), - testonly = kwargs.get("testonly", default = 0), - exec_properties = exec_properties, - ) + These will describe the set of specifications to be used by Proguard. If specified, + they will be added to any android_binary target depending on this library. -def kt_android_local_test( - name, - jvm_flags = None, - manifest = None, - manifest_values = None, - test_class = None, - size = None, - timeout = None, - flaky = False, - shard_count = None, - visibility = None, - testonly = True, - exec_properties = None, - **kwargs): - """Creates a testable Android sandwich library. + The files included here must only have idempotent rules, namely -dontnote, -dontwarn, + assumenosideeffects, and rules that start with -keep. Other options can only appear in + android_binary's proguard_specs, to ensure non-tautological merges.""", + default = [], + allow_files = True, + ), + }.items()), + outputs = _kt_lib_common_outputs_exposed, + toolchains = _common_toolchains, + fragments = ["android", "java"], # Required fragments of the target configuration + host_fragments = ["java"], # Required fragments of the host configuration + implementation = _kt_android_library_impl, + provides = [JavaInfo, _KtJvmInfo], +) - `srcs`, `deps`, `plugins`, `associates` are routed to `kt_jvm_library` the other android - related attributes are handled by the native `android_library` rule while the test attributes - are picked out and handled by the `android_local_test` rule. - """ +kt_android_local_test = rule( + doc = """Setup a simple kotlin_test. - # TODO(556): replace with starlark - # buildifier: disable=native-android - native.android_local_test( - name = name, - deps = kwargs.get("deps", []) + _kt_android_artifact(name = name, testonly = testonly, exec_properties = exec_properties, **kwargs), - jvm_flags = jvm_flags, - test_class = test_class, - visibility = visibility, - size = size, - timeout = timeout, - flaky = flaky, - shard_count = shard_count, - custom_package = kwargs.get("custom_package", default = None), - manifest = manifest, - manifest_values = manifest_values, - tags = kwargs.get("tags", default = None), - testonly = testonly, - exec_properties = exec_properties, - ) + **Notes:** + * The kotlin test library is not added implicitly, it is available with the label + `@com_github_jetbrains_kotlin//:kotlin-test`. + """, + attrs = utils.add_dicts(_kt_android_local_test_runnable_common_attr_exposed, { + "_bazel_test_runner": attr.label( + default = Label("@bazel_tools//tools/jdk:TestRunner_deploy.jar"), + allow_files = True, + ), + "friends": attr.label_list( + doc = """A single Kotlin dep which allows the test code access to internal members. Currently uses the output + jar of the module -- i.e., exported deps won't be included.""", + default = [], + providers = [JavaInfo, _KtJvmInfo], + ), + "test_class": attr.string( + doc = "The Java class to be loaded by the test runner.", + default = "", + ), + "main_class": attr.string(default = "com.google.testing.junit.runner.BazelTestRunner"), + "manifest": attr.label( + doc = """The name of the Android manifest file, normally `AndroidManifest.xml`. + Must be defined if resource_files or assets are defined.""", + default = None, + allow_single_file = True, + ), + "manifest_values": attr.string_dict( + doc = """A dictionary of values to be overridden in the manifest.""", + default = {}), + "custom_package": attr.string( + doc = """Java package for which java sources will be generated. By default the package + is inferred from the directory where the BUILD file containing the rule is. You can + specify a different package but this is highly discouraged since it can introduce + classpath conflicts with other libraries that will only be detected at runtime.""", + ), + "_android_resources_busybox": attr.label( + executable = True, + cfg = "host", + default = Label("@bazel_tools//tools/android:busybox"), + allow_files = True, + ), + "android_sdk": attr.label( + default = Label("@bazel_tools//tools/android:sdk"), + allow_files = True, + ), + # TODO(mgalindo): unify with android_sdk (BUILD-3813) + "_android_sdk": attr.label( + default = Label("@bazel_tools//tools/android:sdk"), + allow_files = True, + ), + "_enable_jdeps": attr.label(default = ":kotlin_deps"), + "_lcov_merger": attr.label( + default = Label("@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main"), + ), + }), + outputs = _kt_lib_common_outputs_exposed, + executable = True, + test = True, + toolchains = _common_toolchains, + fragments = ["android", "java"], # Required fragments of the target configuration + host_fragments = ["java"], # Required fragments of the host configuration + implementation = _kt_android_local_test_impl, +) diff --git a/kotlin/internal/jvm/android_impl.bzl b/kotlin/internal/jvm/android_impl.bzl new file mode 100644 index 000000000..70045dbbb --- /dev/null +++ b/kotlin/internal/jvm/android_impl.bzl @@ -0,0 +1,327 @@ +# 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. +load( + "//kotlin/internal/jvm:compile.bzl", + "export_only_providers", + _compiler_toolchains = "compiler_toolchains_exposed", + _get_kt_dep_infos = "get_kt_dep_infos", + _jvm_deps = "jvm_deps_exposed", + _kt_jvm_produce_output_jar_actions = "kt_jvm_produce_output_jar_actions", +) +load( + "//kotlin/internal:defs.bzl", + _TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE", +) +load( + "//kotlin/internal/jvm:impl.bzl", + _write_launcher_action_exposed = "write_launcher_action_exposed", +) +load( + "//kotlin/internal/jvm:external_java_utils.bzl", + _resolve_package_from_label = "resolve_package_from_label", +) +load( + "//kotlin/internal/jvm:associates.bzl", + _associate_utils = "associate_utils", +) + +load( + ":android_resources.bzl", + _process_resources = "process_resources", + _process_resources_for_android_local_test = "process_resources_for_android_local_test", +) +load( + ":utils.bzl", + "get_transitive_prerequisites", + "utils" +) + + +def _collect_transitive_native_libs(ctx): + infos = get_transitive_prerequisites(ctx, AndroidNativeLibsInfo) + + return depset(transitive = [ + i.native_libs + for i in infos + ]) + +def _collect_proguard_configs(ctx): + """ + Collect transitive proguard configurations from dependencies + """ + infos = get_transitive_prerequisites(ctx, ProguardSpecProvider, ["deps", "exports", "runtime_deps", "plugins"]) + + return depset(ctx.files.proguard_specs, transitive = [ + i.specs + for i in infos + ]) + +def _make_legacy_android_provider(android_ide_info): + # Create the ClassJar "object" for the target.android.idl.output field. + if android_ide_info.idl_class_jar: + idl_class_jar = struct( + class_jar = android_ide_info.idl_class_jar, + ijar = None, + source_jar = android_ide_info.idl_source_jar, + ) + else: + idl_class_jar = None + + return struct( + aar = android_ide_info.aar, + apk = android_ide_info.signed_apk, + apks_under_test = android_ide_info.apks_under_test, + defines_resources = android_ide_info.defines_android_resources, + idl = struct( + import_root = android_ide_info.idl_import_root, + sources = android_ide_info.idl_srcs, + generated_java_files = android_ide_info.idl_generated_java_files, + output = idl_class_jar, + ), + java_package = android_ide_info.java_package, + manifest = android_ide_info.manifest, + merged_manifest = android_ide_info.generated_manifest, + native_libs = android_ide_info.native_libs, + resource_apk = android_ide_info.resource_apk, + resource_jar = android_ide_info.resource_jar, + ) + +def kt_android_library_impl(ctx): + java_package = _resolve_package_from_label(ctx.label, ctx.attr.custom_package, fallback = False) + + resources = None + localRClass = None + generated_manifest = None + + resources = _process_resources(ctx, java_package) + + if ctx.attr.resource_files and resources.r_java: + localRClass = utils.only(utils.list_or_depset_to_list(resources.r_java.compile_jars)) + + if resources.merged_manifest: + generated_manifest = resources.merged_manifest + + resources_providers = resources.providers + + # Setup the compile action. + providers = kt_android_produce_jar_actions(ctx, "kt_jvm_library", localRClass) + + defines_resources = bool( + ctx.attr.manifest or + ctx.attr.resource_files or + ctx.attr.assets or + ctx.attr.assets_dir or + ctx.attr.exports_manifest, + ) + + resource_jar = None + if localRClass: + resource_jar = _getLocalRClass(localRClass).outputs.jars[0] + + # TODO Add the rest of the missing fields to further improve IDE experience + android_ide_info = AndroidIdeInfo( + java_package, # java_package + ctx.file.manifest, # manifest + generated_manifest, # generated_manifest + # TODO add AIDL support: https://jira.sc-corp.net/browse/BUILD-707 + None, # idl_import_root + [], # idl_srcs + [], # idl_generated_java_files + None, # idl_source_jar + None, # idl_class_jar + defines_resources, # defines_android_resources + resource_jar, # resource_jar + None, # resources_apk + None, # signed_apk, always empty for kt_android_library. + None, # aar + [], # apks_under_test, always empty for kt_android_library + dict(), # nativelibs, always empty for kt_android_library + ) + + files = [ctx.outputs.jar] + if providers.java.outputs.jdeps: + files.append(providers.java.outputs.jdeps) + + result = struct( + kt = providers.kt, + android = _make_legacy_android_provider(android_ide_info), + providers = resources_providers + [ + providers.java, + providers.kt, + providers.instrumented_files, + AndroidNativeLibsInfo(_collect_transitive_native_libs(ctx)), + ProguardSpecProvider(_collect_proguard_configs(ctx)), + DefaultInfo( + files = depset(files), + runfiles = ctx.runfiles( + # explicitly include data files, otherwise they appear to be missing + files = ctx.files.data, + transitive_files = depset(order = "default"), + # continue to use collect_default until proper transitive data collecting is + # implemented. + collect_default = True, + ), + ), + ], + ) + return result + +def _getAndroidSdkJar(ctx): + android_jar = ctx.attr.android_sdk[AndroidSdkInfo].android_jar + return JavaInfo(output_jar = android_jar, compile_jar = android_jar, neverlink = True) + +def _getLocalRClass(rClass): + return JavaInfo(output_jar = rClass, compile_jar = rClass, neverlink = True) + + +_SPLIT_STRINGS = [ + "src/test/java/", + "src/test/kotlin/", + "javatests/", + "kotlin/", + "java/", + "test/", +] + +def kt_android_local_test_impl(ctx): + # Android resource processing + java_package = _resolve_package_from_label(ctx.label, ctx.attr.custom_package, fallback = False) + + resources_ctx = _process_resources_for_android_local_test(ctx, java_package) + resources_apk = resources_ctx.resources_apk + resources_jar = resources_ctx.class_jar + + # Generate properties file telling Robolectric from where to load resources. + test_config_file = ctx.actions.declare_file("_robolectric/" + ctx.label.name + "_test_config.properties") + ctx.actions.write( + test_config_file, + content = "android_resource_apk=" + resources_apk.short_path, + ) + + # Setup the compile action. + providers = kt_android_produce_jar_actions( + ctx, + "kt_jvm_library", + extra_resources = {"com/android/tools/test_config.properties": test_config_file}, + ) + + # Create test run action + runtime_jars = depset( + ctx.files._bazel_test_runner + [resources_jar] + [ctx.attr.android_sdk[AndroidSdkInfo].android_jar], + transitive = [providers.java.transitive_runtime_jars], + ) + coverage_runfiles = [] + if ctx.configuration.coverage_enabled: + jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner + coverage_runfiles = jacocorunner.files.to_list() + + test_class = ctx.attr.test_class + + # If no test_class, do a best-effort attempt to infer one. + if not bool(ctx.attr.test_class): + for file in ctx.files.srcs: + package_relative_path = file.path.replace(ctx.label.package + "/", "") + if package_relative_path.split(".")[0] == ctx.attr.name: + for splitter in _SPLIT_STRINGS: + elements = file.short_path.split(splitter, 1) + if len(elements) == 2: + test_class = elements[1].split(".")[0].replace("/", ".") + break + + coverage_metadata = _write_launcher_action_exposed( + ctx, + runtime_jars, + main_class = ctx.attr.main_class, + jvm_flags = [ + "-ea", + "-Dbazel.test_suite=%s" % test_class, + "-Drobolectric.offline=true", + "-Drobolectric-deps.properties=" + _get_android_all_jars_properties_file(ctx).short_path, + ] + ctx.attr.jvm_flags, + ) + + files = [ctx.outputs.jar] + if providers.java.outputs.jdeps: + files.append(providers.java.outputs.jdeps) + + return struct( + kt = providers.kt, + providers = [ + providers.java, + providers.kt, + providers.instrumented_files, + DefaultInfo( + files = depset(files), + runfiles = ctx.runfiles( + # Explicitly include data files, otherwise they appear to be missing + # Include resources apk required by Robolectric + files = ctx.files.data + [resources_apk], + transitive_files = depset( + order = "default", + transitive = [runtime_jars, depset(coverage_runfiles), depset(coverage_metadata)], + direct = ctx.files._java_runtime, + ), + # continue to use collect_default until proper transitive data collecting is + # implmented. + collect_default = True, + ), + ), + ], + ) + +def _get_android_all_jars_properties_file(ctx): + runfiles = ctx.runfiles(collect_data = True).files.to_list() + for run_file in runfiles: + if run_file.basename == "robolectric-deps.properties": + return run_file + fail("'robolectric-deps.properties' not found in the deps of the rule.") + +def kt_android_produce_jar_actions(ctx, rule_kind, rClass = None, extra_resources = {}): + """Setup The actions to compile a jar and if any resources or resource_jars were provided to merge these in with the + compilation output. + + Returns: + see `kt_jvm_compile_action`. + """ + + toolchains = _compiler_toolchains(ctx) + associates = _associate_utils.get_associates(ctx) + dep_infos = _get_kt_dep_infos( + toolchains, + associates.targets, + deps = ctx.attr.deps, + ) + android_dep_infos = [_getAndroidSdkJar(ctx)] + if rClass: + android_dep_infos.append(_getLocalRClass(rClass)) + + compile_deps = _jvm_deps(ctx, android_dep_infos + dep_infos, ctx.attr.runtime_deps) + + # Reset the field used to populate the returned JavaInfo provider as we don't want to include + # other jars from AndroidResourceClassProvider. + compile_deps.provided_deps.clear() + compile_deps.provided_deps.extend(dep_infos) + + # Setup the compile action. + return _kt_jvm_produce_output_jar_actions( + ctx, + rule_kind = rule_kind, + compile_deps = compile_deps, + extra_resources = extra_resources, + ) if ctx.attr.srcs else export_only_providers( + ctx = ctx, + actions = ctx.actions, + outputs = ctx.outputs, + attr = ctx.attr, + ) diff --git a/kotlin/internal/jvm/android_resources.bzl b/kotlin/internal/jvm/android_resources.bzl new file mode 100644 index 000000000..06fc7f639 --- /dev/null +++ b/kotlin/internal/jvm/android_resources.bzl @@ -0,0 +1,79 @@ +load( + ":utils.bzl", + "get_transitive_prerequisites", + "get_android_sdk", + "get_android_toolchain", + "get_host_javabase", + "get_java_toolchain", + "utils") +load("@io_bazel_rules_android//rules:resources.bzl", _rules_android_resources = "resources") +load("@io_bazel_rules_android//rules/android_local_test:resources.bzl", _rules_android_local_test_processors = "PROCESSORS_FOR_ANDROID_LOCAL_TEST") + +TRISTATE_AUTO = -1 +TRISTATE_YES = 1 + +MANIFEST_PROCESSOR = "ManifestProcessor" # this is not really needed but using it to simplify integration +RESOURCE_PROCESSOR = "ResourceProcessor" + +# Executes the starlark rules_android resource processing pipeline +def process_resources(ctx, java_package): + + """Collects the transitive dependencies from passed attributes. + + Args: + ctx: The context. + java_package: the java package of the target + + Returns: + A resource_ctx dict of resources_providers. + """ + # exports_manifest can be overridden by a bazel flag. + if ctx.attr.exports_manifest == TRISTATE_AUTO: + exports_manifest = ctx.fragments.android.get_exports_manifest_default + else: + exports_manifest = ctx.attr.exports_manifest == TRISTATE_YES + + return _rules_android_resources.process( + ctx, + manifest = ctx.file.manifest, + resource_files = ctx.attr.resource_files, + assets = ctx.attr.assets, + assets_dir = ctx.attr.assets_dir, + exports_manifest = exports_manifest, + java_package = java_package, + custom_package = ctx.attr.custom_package, + neverlink = ctx.attr.neverlink, + enable_data_binding = False, #ctx.attr.enable_data_binding, + deps = ctx.attr.deps, + exports = ctx.attr.exports, + + # Processing behavior changing flags. + enable_res_v3 = False, #_flags.get(ctx).android_enable_res_v3, + # TODO(b/144163743): remove fix_resource_transitivity, which was only added to emulate + # misbehavior on the Java side. + fix_resource_transitivity = bool(ctx.attr.srcs), + fix_export_exporting = True, #acls.in_fix_export_exporting_rollout(str(ctx.label)), + propagate_resources = True, # not ctx.attr._android_test_migration, + + # Tool and Processing related inputs + aapt = get_android_toolchain(ctx).aapt2.files_to_run, + android_jar = get_android_sdk(ctx).android_jar, + android_kit = get_android_toolchain(ctx).android_kit.files_to_run, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + java_toolchain = get_java_toolchain(ctx), + host_javabase = get_host_javabase(ctx), + instrument_xslt = False, #utils.only(get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()), + res_v3_dummy_manifest = utils.only( + get_android_toolchain(ctx).res_v3_dummy_manifest.files.to_list(), + ), + res_v3_dummy_r_txt = utils.only( + get_android_toolchain(ctx).res_v3_dummy_r_txt.files.to_list(), + ), + xsltproc = None, #get_android_toolchain(ctx).xsltproc_tool.files_to_run, + zip_tool = get_android_toolchain(ctx).zip_tool.files_to_run, + ) + +def process_resources_for_android_local_test(ctx, java_package): + manifest_ctx = _rules_android_local_test_processors[MANIFEST_PROCESSOR](ctx).value + return _rules_android_local_test_processors[RESOURCE_PROCESSOR](ctx, manifest_ctx, java_package).value + diff --git a/kotlin/internal/jvm/compile.bzl b/kotlin/internal/jvm/compile.bzl index f6b4e0da9..d6e1c2527 100644 --- a/kotlin/internal/jvm/compile.bzl +++ b/kotlin/internal/jvm/compile.bzl @@ -90,7 +90,31 @@ 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 _compiler_friends(ctx, friends): + """Creates a struct of friends meta data""" + + # 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 get_kt_dep_infos(toolchains, associated_targets, deps): """Encapsulates jvm dependency metadata.""" diff = _sets.intersection( _sets.copy_of([x.label for x in associated_targets]), @@ -102,16 +126,23 @@ 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] + return dep_infos + +def _jvm_deps(ctx, dep_infos, runtime_deps): + """Encapsulates compiler dependency metadata.""" + transitive = [ + d.compile_jars + for d in dep_infos + ] + [ + d.transitive_compile_time_jars + for d in dep_infos + ] + return struct( deps = dep_infos, + provided_deps = dep_infos[:], # make a copy otherwise modifying one will modify the other 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], ) @@ -198,7 +229,7 @@ def _fold_jars_action(ctx, rule_kind, toolchains, output_jar, input_jars, action ), ) -def _resourcejar_args_action(ctx): +def _resourcejar_args_action(ctx, extra_resources = {}): res_cmd = [] for f in ctx.files.resources: target_path = _adjust_resources_path(f.short_path, ctx.attr.resource_strip_prefix) @@ -209,20 +240,31 @@ def _resourcejar_args_action(ctx): f_path = f.path, ) res_cmd.extend([line]) + + for key, value in extra_resources.items(): + target_path = _adjust_resources_path(value.short_path, ctx.label.package) + if target_path[0] == "/": + target_path = target_path[1:] + line = "{target_path}={res_path}\n".format( + res_path = value.path, + target_path = key, + ) + res_cmd.extend([line]) + zipper_args_file = ctx.actions.declare_file("%s_resources_zipper_args" % ctx.label.name) ctx.actions.write(zipper_args_file, "".join(res_cmd)) return zipper_args_file -def _build_resourcejar_action(ctx): +def _build_resourcejar_action(ctx, extra_resources = {}): """sets up an action to build a resource jar for the target being compiled. Returns: The file resource jar file. """ resources_jar_output = ctx.actions.declare_file(ctx.label.name + "-resources.jar") - zipper_args = _resourcejar_args_action(ctx) + zipper_args = _resourcejar_args_action(ctx, extra_resources) ctx.actions.run_shell( mnemonic = "KotlinZipResourceJar", - inputs = ctx.files.resources + [zipper_args], + inputs = ctx.files.resources + extra_resources.values() + [zipper_args], tools = [ctx.executable._zipper], outputs = [resources_jar_output], command = "{zipper} c {resources_jar_output} @{path}".format( @@ -461,7 +503,30 @@ def _run_kt_builder_action( # MAIN ACTIONS ######################################################################################################### -def kt_jvm_produce_jar_actions(ctx, rule_kind): +def kt_jvm_produce_jar_actions(ctx, rule_kind, extra_resources = {}): + """Setup The actions to compile a jar and if any resources or resource_jars were provided to merge these in with the + compilation output. + + Returns: + see `kt_jvm_compile_action`. + """ + + toolchains = _compiler_toolchains(ctx) + associates = _associate_utils.get_associates(ctx) + dep_infos = get_kt_dep_infos( + toolchains, + associates.targets, + deps = ctx.attr.deps) + + # Setup the compile action. + return kt_jvm_produce_output_jar_actions( + ctx, + rule_kind = rule_kind, + compile_deps = _jvm_deps(ctx, dep_infos, ctx.attr.runtime_deps), + extra_resources = extra_resources + ) + +def kt_jvm_produce_output_jar_actions(ctx, rule_kind, compile_deps, extra_resources = {}): """This macro sets up a compile action for a Kotlin jar. Args: @@ -474,12 +539,6 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): toolchains = _compiler_toolchains(ctx) srcs = _partitioned_srcs(ctx.files.srcs) associates = _associate_utils.get_associates(ctx) - compile_deps = _jvm_deps( - toolchains, - 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) @@ -513,8 +572,8 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): annotation_processing = outputs_struct.annotation_processing # If this rule has any resources declared setup a zipper action to turn them into a jar. - if len(ctx.files.resources) > 0: - output_jars.append(_build_resourcejar_action(ctx)) + if len(ctx.files.resources) + len(extra_resources) > 0: + output_jars.append(_build_resourcejar_action(ctx, extra_resources)) output_jars.extend(ctx.files.resource_jars) # Merge outputs into final runtime jar. @@ -541,7 +600,7 @@ def kt_jvm_produce_jar_actions(ctx, rule_kind): compile_jar = compile_jar, source_jar = source_jar, jdeps = output_jdeps, - deps = compile_deps.deps, + deps = compile_deps.provided_deps, runtime_deps = [_java_info(d) for d in ctx.attr.runtime_deps], exports = [_java_info(d) for d in getattr(ctx.attr, "exports", [])], neverlink = getattr(ctx.attr, "neverlink", False), @@ -836,3 +895,11 @@ def export_only_providers(ctx, actions, attr, outputs): extensions = ["kt", "java"], ), ) + +# +# Exposed for kt_android_* rules +# +jvm_deps_exposed = _jvm_deps +compiler_friends_exposed = _compiler_friends +compiler_toolchains_exposed = _compiler_toolchains + diff --git a/kotlin/internal/jvm/external_java_utils.bzl b/kotlin/internal/jvm/external_java_utils.bzl new file mode 100644 index 000000000..952e290ff --- /dev/null +++ b/kotlin/internal/jvm/external_java_utils.bzl @@ -0,0 +1,110 @@ +# 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. + +"""Various Java utils that were copied from pre-alpha branch of rules_android repo + + See: + https://github.com/bazelbuild/rules_android/blob/pre-alpha/rules/java.bzl +""" + +# TODO(djwhang): Get the path separator in a platform agnostic manner. +_PATH_SEP = "/" + +def _is_absolute(path): + # TODO(djwhang): This is not cross platform safe. Windows absolute paths + # do not start with "//", rather "C:\". + return path.startswith(_PATH_SEP) + +def _segment_idx(path_segments): + """Finds the index of the segment in the path that preceeds the source root. + Args: + path_segments: A list of strings, where each string is the segment of a + filesystem path. + Returns: + An index to the path segment that represents the Java segment or -1 if + none found. + """ + if _is_absolute(path_segments[0]): + fail("ERROR: path must not be absolute: %s" % "/".join(path_segments)) + + root_idx = -1 + for idx, segment in enumerate(path_segments): + if segment in ["java", "javatests", "src", "testsrc"]: + root_idx = idx + break + if root_idx < 0: + return root_idx + + is_src = path_segments[root_idx] == "src" + check_maven_idx = root_idx if is_src else -1 + if root_idx == 0 or is_src: + # Check for a nested root directory. + for idx in range(root_idx + 1, len(path_segments) - 2): + segment = path_segments[idx] + if segment == "src" or (is_src and segment in ["java", "javatests"]): + next_segment = path_segments[idx + 1] + if next_segment in ["com", "org", "net"]: + root_idx = idx + elif segment == "src": + check_maven_idx = idx + break + + if check_maven_idx >= 0 and check_maven_idx + 2 < len(path_segments): + next_segment = path_segments[check_maven_idx + 1] + if next_segment in ["main", "test"]: + next_segment = path_segments[check_maven_idx + 2] + if next_segment in ["java", "resources"]: + root_idx = check_maven_idx + 2 + return root_idx + +def _resolve_package(path): + """Determines the Java package name from the given path. + Examples: + "{workspace}/java/foo/bar/wiz" -> "foo.bar.wiz" + "{workspace}/javatests/foo/bar/wiz" -> "foo.bar.wiz" + Args: + path: A string, representing a file path. + Returns: + A string representing a Java package name or None if could not be + determined. + """ + path_segments = path.partition(":")[0].split("/") + java_idx = _segment_idx(path_segments) + if java_idx < 0: + return None + else: + return ".".join(path_segments[java_idx + 1:]) + +def resolve_package_from_label( + label, + custom_package = None, + fallback = True): + """Resolves the Java package from a Label. + When no legal Java package can be resolved from the label, None will be + returned unless fallback is specified. + When a fallback is requested, a not safe for Java compilation package will + be returned. The fallback value will be derrived by taking the label.package + and replacing all path separators with ".". + """ + if custom_package: + return custom_package + + java_package = _resolve_package(str(label)) + if java_package != None: # "" is a valid result. + return java_package + + if fallback: + return label.package.replace("/", ".") + + return None diff --git a/kotlin/internal/jvm/impl.bzl b/kotlin/internal/jvm/impl.bzl index 107f39087..86237f80a 100644 --- a/kotlin/internal/jvm/impl.bzl +++ b/kotlin/internal/jvm/impl.bzl @@ -126,6 +126,9 @@ def _write_launcher_action(ctx, rjars, main_class, jvm_flags): ) return [] +# Exposed for kt_android_* rules +write_launcher_action_exposed = _write_launcher_action + # buildifier: disable=unused-variable def _is_source_jar_stub(jar): """Workaround for intellij plugin expecting a source jar""" diff --git a/kotlin/internal/jvm/jvm.bzl b/kotlin/internal/jvm/jvm.bzl index 93d345558..71b272047 100644 --- a/kotlin/internal/jvm/jvm.bzl +++ b/kotlin/internal/jvm/jvm.bzl @@ -559,3 +559,10 @@ Supports the following template values: implementation = _kt_compiler_plugin_impl, provides = [_KtCompilerPluginInfo], ) + +# +# Exposed for kt_android_* rules. +# +lib_common_attr_exposed = _lib_common_attr +runnable_common_attr_exposed = _runnable_common_attr +common_outputs_exposed = _common_outputs diff --git a/kotlin/internal/jvm/utils.bzl b/kotlin/internal/jvm/utils.bzl new file mode 100644 index 000000000..c1840aadb --- /dev/null +++ b/kotlin/internal/jvm/utils.bzl @@ -0,0 +1,68 @@ +load( + "//kotlin/internal:defs.bzl", + _TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE", +) + +def get_transitive_prerequisites(ctx, provider, attributes = ["deps", "exports"]): + """Collects the transitive dependencies from passed attributes. + + Args: + ctx: The context. + provider: the provider to collect from the attributes. + attributes: the list of attributes from context. + + Returns: + A list of all the collected providers + """ + result = [] + for attribute in attributes: + for target in getattr(ctx.attr, attribute): + if provider in target: + result.append(target[provider]) + return result + +def get_java_toolchain(ctx): + if not hasattr(ctx.attr, "_java_toolchain"): + fail("Missing _java_toolchain attr") + return ctx.attr._java_toolchain + +def get_host_javabase(ctx): + if not hasattr(ctx.attr, "_host_javabase"): + fail("Missing _host_javabase attr") + return ctx.attr._host_javabase + +def get_android_toolchain(ctx): + return ctx.toolchains["@io_bazel_rules_android//toolchains/android:toolchain_type"] + +def get_android_sdk(ctx): + if getattr(ctx.fragments.android, "incompatible_use_toolchain_resolution", False): + return ctx.toolchains["@io_bazel_rules_android//toolchains/android_sdk:toolchain_type"].android_sdk_info + else: + return ctx.attr.android_sdk[AndroidSdkInfo] + +def _only(collection): + """Returns the only item in the collection.""" + if len(collection) != 1: + fail("Expected one element, has %s." % len(collection)) + return _first(collection) + +def _first(collection): + """Returns the first item in the collection.""" + for i in collection: + return i + return fail("The collection is empty.") + +def _list_or_depset_to_list(list_or_depset): + if type(list_or_depset) == "list": + return list_or_depset + elif type(list_or_depset) == "depset": + return list_or_depset.to_list() + else: + return fail("Expected a list or a depset. Got %s" % type(list_or_depset)) + +utils = struct( + only = _only, + first = _first, + list_or_depset_to_list = _list_or_depset_to_list, +) + diff --git a/src/main/starlark/core/repositories/download.bzl b/src/main/starlark/core/repositories/download.bzl index 2660ceadb..3a6718377 100644 --- a/src/main/starlark/core/repositories/download.bzl +++ b/src/main/starlark/core/repositories/download.bzl @@ -104,7 +104,10 @@ def kt_download_local_dev_dependencies(): strip_prefix = "rules_android-%s" % versions.ANDROID.VERSION, urls = versions.ANDROID.URLS, starlark_packages = [ - "android", + "src", + "rules", + "toolchains", + "tools", ], ) diff --git a/src/main/starlark/core/repositories/setup.bzl b/src/main/starlark/core/repositories/setup.bzl index d978d8c90..53043520a 100644 --- a/src/main/starlark/core/repositories/setup.bzl +++ b/src/main/starlark/core/repositories/setup.bzl @@ -16,7 +16,7 @@ load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_ load("@rules_jvm_external//:defs.bzl", "maven_install") load("@io_bazel_stardoc//:setup.bzl", "stardoc_repositories") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") -load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository") +load("@build_bazel_rules_android//rules:rules.bzl", "android_sdk_repository") load("//src/main/starlark/core/repositories:versions.bzl", "versions") def kt_configure(): diff --git a/src/main/starlark/core/repositories/versions.bzl b/src/main/starlark/core/repositories/versions.bzl index 9536efbac..4a34fe82e 100644 --- a/src/main/starlark/core/repositories/versions.bzl +++ b/src/main/starlark/core/repositories/versions.bzl @@ -25,8 +25,8 @@ versions = struct( PROTOBUF_SHA = "cf754718b0aa945b00550ed7962ddc167167bd922b842199eeb6505e6f344852", BAZEL_DEPS_VERSION = "0.1.0", BAZEL_DEPS_SHA = "05498224710808be9687f5b9a906d11dd29ad592020246d4cd1a26eeaed0735e", - RULES_JVM_EXTERNAL_TAG = "4.2", - RULES_JVM_EXTERNAL_SHA = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca", + RULES_JVM_EXTERNAL_TAG = "4.4.2", + RULES_JVM_EXTERNAL_SHA = "735602f50813eb2ea93ca3f5e43b1959bd80b213b836a07a62a29d757670b77b", RULES_PROTO_GIT_COMMIT = "f6b8d89b90a7956f6782a4a3609b2f0eee3ce965", RULES_PROTO_SHA = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0", IO_BAZEL_STARDOC_VERSION = "0.5.1", @@ -47,9 +47,9 @@ versions = struct( sha256 = "5e3c8d0f965410ff12e90d6f8dc5df2fc09fd595a684d514616851ce7e94ae7d", ), ANDROID = struct( - VERSION = "0.1.1", - SHA = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", - URLS = ["https://github.com/bazelbuild/rules_android/archive/v%s.zip" % "0.1.1"], + VERSION = "pre-alpha-01-11-2022-3", + SHA = "2f5c455937d647065f31d83e3b760d087bbf6e433e9291dbe3b62c999e2991be", + URLS = ["https://github.com/mauriciogg/rules_android/archive/refs/tags/pre-alpha-01-11-2022-3.zip"], BUILD_TOOLS = "30.0.3", ), PYTHON = struct( From b550b26676a53695e238267ebcbdfd7160efbeeb Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Tue, 1 Nov 2022 17:08:45 -0700 Subject: [PATCH 2/4] Check in android application example --- examples/android/app/BUILD.bazel | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/examples/android/app/BUILD.bazel b/examples/android/app/BUILD.bazel index d8c7b83c2..4882ceac0 100644 --- a/examples/android/app/BUILD.bazel +++ b/examples/android/app/BUILD.bazel @@ -1,5 +1,5 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@io_bazel_rules_android//rules:rules.bzl", "android_binary") +load("@io_bazel_rules_android//rules:rules.bzl", "android_binary", "android_application") # An app that consumes android-kt deps android_binary( @@ -16,6 +16,19 @@ android_binary( ], ) +android_application( + name = "application", + custom_package = "examples.android.app", + manifest = "src/main/AndroidManifest.xml", + manifest_values = { + "lib_name": "lib", + }, + multidex = "native", + deps = [ + "//libKtAndroid:my_kt", + ], +) + # An app that consumes jvm-kt libs android_binary( name = "app2", From 4b2cd745d1681703654fedb349b880a3f085f083 Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Tue, 1 Nov 2022 17:31:28 -0700 Subject: [PATCH 3/4] Generate app bundle --- examples/android/app/BUILD.bazel | 2 + examples/android/app/bundleConfig.pb.json | 46 +++++++++++++++++++ .../android/app/src/main/AndroidManifest.xml | 3 +- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 examples/android/app/bundleConfig.pb.json diff --git a/examples/android/app/BUILD.bazel b/examples/android/app/BUILD.bazel index 4882ceac0..5330f9c8b 100644 --- a/examples/android/app/BUILD.bazel +++ b/examples/android/app/BUILD.bazel @@ -19,9 +19,11 @@ android_binary( android_application( name = "application", custom_package = "examples.android.app", + bundle_config_file = "bundleConfig.pb.json", manifest = "src/main/AndroidManifest.xml", manifest_values = { "lib_name": "lib", + "applicationId": "examples.android.app", }, multidex = "native", deps = [ diff --git a/examples/android/app/bundleConfig.pb.json b/examples/android/app/bundleConfig.pb.json new file mode 100644 index 000000000..2712c5212 --- /dev/null +++ b/examples/android/app/bundleConfig.pb.json @@ -0,0 +1,46 @@ +{ + "optimizations": { + "uncompressNativeLibraries": { "enabled": false }, + "standaloneConfig": { + "dexMergingStrategy": "NEVER_MERGE" + } + }, + "compression": { + "uncompressedGlob": [ + "**/*.3g2", + "**/*.3gp", + "**/*.3gpp", + "**/*.3gpp2", + "**/*.aac", + "**/*.amr", + "**/*.awb", + "**/*.gif", + "**/*.imy", + "**/*.jet", + "**/*.jpeg", + "**/*.jpg", + "**/*.m4a", + "**/*.m4v", + "**/*.mid", + "**/*.midi", + "**/*.mkv", + "**/*.mp2", + "**/*.mp3", + "**/*.mp4", + "**/*.mpeg", + "**/*.mpg", + "**/*.ogg", + "**/*.png", + "**/*.rtttl", + "**/*.smf", + "**/*.so.xz", + "**/*.so.zst", + "**/*.wav", + "**/*.webm", + "**/*.wma", + "**/*.wmv", + "**/*.xmf", + "**/*.zst.so" + ] + } +} diff --git a/examples/android/app/src/main/AndroidManifest.xml b/examples/android/app/src/main/AndroidManifest.xml index e7a35394a..6cca50b4a 100644 --- a/examples/android/app/src/main/AndroidManifest.xml +++ b/examples/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ - \ No newline at end of file + From f9b614e5140352265adec20afd40f65befee433b Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Fri, 4 Nov 2022 16:08:08 -0700 Subject: [PATCH 4/4] Adress comments --- .bazelrc | 3 +++ examples/android/.bazelrc | 2 ++ examples/android/WORKSPACE | 2 ++ examples/android/libKtAndroid/BUILD.bazel | 12 ++++++++++++ examples/anvil/.bazelrc | 2 ++ examples/jetpack_compose/.bazelrc | 2 ++ examples/plugin/.bazelrc | 2 ++ examples/plugin/WORKSPACE | 2 -- kotlin/internal/jvm/android.bzl | 3 +++ 9 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.bazelrc b/.bazelrc index 84ebabe9e..060ee65fe 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,5 +1,8 @@ build --strategy=KotlinCompile=worker build --test_output=all build --verbose_failures + +# Expose (not really) experimental Android providers need by rules_android- +# AndroidLibraryResourceClassJarProvider, AndroidIdeInfo, ... build --experimental_google_legacy_api diff --git a/examples/android/.bazelrc b/examples/android/.bazelrc index d5b238e6c..0c1831910 100644 --- a/examples/android/.bazelrc +++ b/examples/android/.bazelrc @@ -1 +1,3 @@ +# Expose (not really) experimental Android providers need by rules_android- +# AndroidLibraryResourceClassJarProvider, AndroidIdeInfo, ... build --experimental_google_legacy_api diff --git a/examples/android/WORKSPACE b/examples/android/WORKSPACE index dd743a3c8..b663594e9 100644 --- a/examples/android/WORKSPACE +++ b/examples/android/WORKSPACE @@ -60,6 +60,8 @@ maven_install( aar_import_bzl_label = "@io_bazel_rules_android//rules:rules.bzl", ) +# Pulling from personal branch since we need a few fixes in rules_android +# A non complete list can be seen here https://github.com/bazelbuild/rules_android/pulls http_archive( name = "io_bazel_rules_android", sha256 = versions.ANDROID.SHA, diff --git a/examples/android/libKtAndroid/BUILD.bazel b/examples/android/libKtAndroid/BUILD.bazel index 6a4205020..0da0b887f 100644 --- a/examples/android/libKtAndroid/BUILD.bazel +++ b/examples/android/libKtAndroid/BUILD.bazel @@ -37,3 +37,15 @@ kt_android_library( "@maven//:org_jetbrains_kotlinx_kotlinx_serialization_runtime", ], ) + +# Commenting out- rules_android resource processing wil produce +# two resource classes with the same package and upstream targets +# that depend on res and res2 will only see definitions from +# the jar that appears firs in the classpaht resulting in +# field not found exceptions. +#android_library( +# name = "res2", +# custom_package = "examples.android.lib", +# manifest = "src/main/AndroidManifest.xml", +# resource_files = glob(["res2/**"]), +#) diff --git a/examples/anvil/.bazelrc b/examples/anvil/.bazelrc index 44a921772..e7ae0740a 100644 --- a/examples/anvil/.bazelrc +++ b/examples/anvil/.bazelrc @@ -1 +1,3 @@ +# Expose (not really) experimental Android providers need by rules_android- +# AndroidLibraryResourceClassJarProvider, AndroidIdeInfo, ... common --experimental_google_legacy_api diff --git a/examples/jetpack_compose/.bazelrc b/examples/jetpack_compose/.bazelrc index fc47b9241..17a4a817b 100644 --- a/examples/jetpack_compose/.bazelrc +++ b/examples/jetpack_compose/.bazelrc @@ -6,5 +6,7 @@ build --define=android_incremental_dexing_tool=d8_dexbuilder build --define=android_standalone_dexing_tool=d8_compat_dx build --nouse_workers_with_dexbuilder +# Expose (not really) experimental Android providers need by rules_android- +# AndroidLibraryResourceClassJarProvider, AndroidIdeInfo, ... common --experimental_google_legacy_api diff --git a/examples/plugin/.bazelrc b/examples/plugin/.bazelrc index 44a921772..e7ae0740a 100644 --- a/examples/plugin/.bazelrc +++ b/examples/plugin/.bazelrc @@ -1 +1,3 @@ +# Expose (not really) experimental Android providers need by rules_android- +# AndroidLibraryResourceClassJarProvider, AndroidIdeInfo, ... common --experimental_google_legacy_api diff --git a/examples/plugin/WORKSPACE b/examples/plugin/WORKSPACE index dccddafe7..496bbfe02 100644 --- a/examples/plugin/WORKSPACE +++ b/examples/plugin/WORKSPACE @@ -53,8 +53,6 @@ load("@io_bazel_rules_android//:defs.bzl", "rules_android_workspace") rules_android_workspace() -#register_toolchains("//bzl/android:android_default_toolchain") - # Setup a custom toolchain from //bzl/kotlin:kotlin_toolchain register_toolchains( "@io_bazel_rules_android//toolchains/android:android_default_toolchain", diff --git a/kotlin/internal/jvm/android.bzl b/kotlin/internal/jvm/android.bzl index ad9ad3b99..eda269261 100644 --- a/kotlin/internal/jvm/android.bzl +++ b/kotlin/internal/jvm/android.bzl @@ -167,6 +167,9 @@ kt_android_local_test = rule( allow_files = True, ), # TODO(mgalindo): unify with android_sdk (BUILD-3813) + # rules_android uses ctx._android_sdk rules_kotlin uses ctx.android_sdk + # rules_android should just use ctx.android_sdk and then this should + # be deleted. "_android_sdk": attr.label( default = Label("@bazel_tools//tools/android:sdk"), allow_files = True,