| load(":coverage.bzl", "coverage_baseline", "coverage_java_test") |
| load(":functions.bzl", "explicit_target") |
| load(":merge_archives.bzl", "merge_jars") |
| load(":lint.bzl", "lint_test") |
| load(":merge_archives.bzl", "run_singlejar") |
| load("@bazel_tools//tools/jdk:toolchain_utils.bzl", "find_java_runtime_toolchain", "find_java_toolchain") |
| load(":functions.bzl", "create_option_file") |
| |
| def test_kotlin_use_ir(): |
| return select({ |
| "//tools/base/bazel:kotlin_no_use_ir": False, |
| "//conditions:default": True, |
| }) |
| |
| def kotlin_compile(ctx, name, srcs, deps, friends, out, jre, transitive_classpath): |
| """Runs kotlinc on the given source files. |
| |
| Args: |
| ctx: the analysis context |
| name: the name of the module being compiled |
| srcs: a list of Java and Kotlin source files |
| deps: a list of JavaInfo providers from direct dependencies |
| friends: a list of friend jars (allowing access to 'internal' members) |
| out: the output jar file |
| jre: list of jars from the JRE bootclasspath |
| transitive_classpath: whether to include transitive deps in the compile classpath |
| |
| Returns: |
| JavaInfo for the resulting jar. |
| |
| Expects that ctx.files._kotlinc is defined. |
| |
| Note: kotlinc only compiles Kotlin, not Java. So if there are Java |
| sources, then you will also need to run javac after this action. |
| """ |
| |
| # TODO: Either disable transitive_classpath in all cases, or otherwise |
| # implement strict-deps enforcement for Kotlin to ensure that targets |
| # declare dependencies on everything they directly use. |
| if transitive_classpath: |
| deps = [java_common.make_non_strict(dep) for dep in deps] |
| |
| # Normally ABI jars are used at compile time, but the ABI jars generated by |
| # ijar are incorrect for Kotlin because (for example) kotlinc expects to see |
| # the bodies of inline functions in class files. So, we have to explicitly |
| # put 'full_compile_jars' on the classpath instead. |
| # TODO: Support ABI jars for Kotlin. |
| dep_jars = depset(transitive = [dep.full_compile_jars for dep in deps]) |
| |
| args = ctx.actions.args() |
| |
| args.add("-module-name", name) |
| args.add("-nowarn") # Mirrors the default javac opts. |
| args.add("-jvm-target", "1.8") |
| args.add("-api-version", "1.4") |
| args.add("-language-version", "1.4") |
| args.add("-Xjvm-default=enable") |
| args.add("-no-stdlib") |
| |
| # Dependency jars may be compiled with a new kotlinc IR backend. |
| args.add("-Xallow-unstable-dependencies") |
| |
| # Add "use-ir" to enable the new IR backend for kotlinc tasks when the |
| # attribute "kotlin_use_ir" is set |
| if ctx.attr.kotlin_use_ir: |
| args.add("-Xuse-ir") |
| |
| # Use custom JRE instead of the default one picked by kotlinc. |
| args.add("-no-jdk") |
| classpath = depset(direct = jre, transitive = [dep_jars]) |
| |
| # Note: there are some open questions regarding the transitivity of friends. |
| # See https://github.com/bazelbuild/rules_kotlin/issues/211. |
| args.add_joined(friends, join_with = ",", format_joined = "-Xfriend-paths=%s") |
| |
| args.add_joined("-cp", classpath, join_with = ":") |
| args.add("-o", out) |
| args.add_all(srcs) |
| |
| # To enable persistent Bazel workers, all arguments must come in an argfile. |
| args.use_param_file("@%s", use_always = True) |
| args.set_param_file_format("multiline") |
| |
| ctx.actions.run( |
| inputs = depset(direct = srcs, transitive = [classpath]), |
| outputs = [out], |
| mnemonic = "kotlinc", |
| arguments = [args], |
| executable = ctx.executable._kotlinc, |
| execution_requirements = {"supports-workers": "1"}, |
| ) |
| |
| return JavaInfo(output_jar = out, compile_jar = out) |
| |
| def kotlin_test( |
| name, |
| srcs, |
| deps = [], |
| runtime_deps = [], |
| friends = [], |
| visibility = None, |
| lint_baseline = None, |
| lint_classpath = [], |
| **kwargs): |
| kotlin_library( |
| name = name + ".testlib", |
| srcs = srcs, |
| deps = deps, |
| testonly = True, |
| runtime_deps = runtime_deps, |
| jar_name = name + ".jar", |
| lint_baseline = lint_baseline, |
| lint_classpath = lint_classpath, |
| lint_is_test_sources = True, |
| visibility = visibility, |
| friends = friends, |
| ) |
| |
| coverage_java_test( |
| name = name + ".test", |
| runtime_deps = [ |
| ":" + name + ".testlib", |
| ] + runtime_deps, |
| visibility = visibility, |
| **kwargs |
| ) |
| |
| native.test_suite( |
| name = name, |
| tests = [name + ".test"], |
| ) |
| |
| # Creates actions to generate the sources jar |
| def _sources(ctx, srcs, source_jars, jar, java_toolchain, host_javabase): |
| java_common.pack_sources( |
| ctx.actions, |
| output_source_jar = jar, |
| sources = srcs, |
| source_jars = source_jars, |
| java_toolchain = java_toolchain, |
| host_javabase = host_javabase, |
| ) |
| |
| # Creates actions to generate a resources_jar from the given resources. |
| def _resources(ctx, resources, notice, resources_jar): |
| prefix = ctx.attr.resource_strip_prefix |
| rel_paths = [] |
| if notice: |
| rel_paths.append((notice.basename, notice)) |
| for res in resources: |
| short = res.short_path |
| if short.startswith(prefix): |
| short = short[len(prefix):] |
| if short.startswith("/"): |
| short = short[1:] |
| rel_paths.append((short, res)) |
| zipper_args = ["cC" if ctx.attr.compress_resources else "c", resources_jar.path] |
| zipper_files = "".join([k + "=" + v.path + "\n" for k, v in rel_paths]) |
| zipper_list = create_option_file(ctx, resources_jar.basename + ".res.lst", zipper_files) |
| zipper_args += ["@" + zipper_list.path] |
| ctx.actions.run( |
| inputs = resources + ([notice] if notice else []) + [zipper_list], |
| outputs = [resources_jar], |
| executable = ctx.executable._zipper, |
| arguments = zipper_args, |
| progress_message = "Creating resources %s (%d files)" % (resources_jar.short_path, len(resources)), |
| mnemonic = "zipper", |
| ) |
| |
| def kotlin_library( |
| name, |
| srcs, |
| deps = None, |
| javacopts = [], |
| jar_name = None, |
| lint_baseline = None, |
| lint_classpath = [], |
| lint_is_test_sources = False, |
| lint_timeout = None, |
| compress_resources = False, |
| testonly = False, |
| stdlib = "@maven//:org.jetbrains.kotlin.kotlin-stdlib", |
| **kwargs): |
| """Compiles a library jar from Java and Kotlin sources |
| |
| Args: |
| srcs: The sources of the library. |
| javacopts: Additional javac options. |
| resources: Resources to add to the jar. |
| resources_strip_prefix: The prefix to strip from the resources path. |
| deps: The dependencies of this library. |
| runtime_deps: The runtime dependencies. |
| bundled_deps: The dependencies that are bundled inside the output jar and not treated as a maven dependency |
| friends: The list of kotlin-friends. |
| compress_resources: Whether to compress resources. |
| notice: An optional notice file to be included in the jar. |
| coordinates: The maven coordinates of this artifact. |
| exclusions: Files to exclude from the generated pom file. |
| lint_*: Lint configuration arguments |
| module_name: The kotlin module name. |
| """ |
| |
| kotlins = [src for src in srcs if src.endswith(".kt")] |
| javas = [src for src in srcs if src.endswith(".java")] |
| source_jars = [src for src in srcs if src.endswith(".srcjar")] |
| final_javacopts = javacopts + ["--release", "8"] |
| |
| # Include non-test kotlin libraries in coverage |
| if not testonly: |
| coverage_baseline( |
| name = name, |
| srcs = javas + kotlins, |
| ) |
| |
| _kotlin_library( |
| name = name, |
| jar = jar_name if jar_name else "lib" + name + ".jar", |
| java_srcs = javas, |
| deps = deps, |
| kotlin_srcs = kotlins, |
| source_jars = source_jars, |
| compress_resources = compress_resources, |
| kotlin_use_ir = test_kotlin_use_ir(), |
| javacopts = final_javacopts if javas else None, |
| testonly = testonly, |
| stdlib = stdlib, |
| **kwargs |
| ) |
| |
| # TODO move lint tests out of here |
| if lint_baseline: |
| # TODO: use srcs once the migration is completed |
| lint_srcs = javas + kotlins |
| if not lint_srcs: |
| fail("lint_baseline set for rule that has no sources") |
| |
| lint_test( |
| name = name + "_lint_test", |
| srcs = lint_srcs, |
| baseline = lint_baseline, |
| deps = deps + lint_classpath, |
| custom_rules = ["//tools/base/lint:studio-checks.lint-rules.jar"], |
| tags = ["no_windows"], |
| is_test_sources = lint_is_test_sources, |
| timeout = lint_timeout if lint_timeout else None, |
| ) |
| |
| def _kotlin_library_impl(ctx): |
| java_srcs = ctx.files.java_srcs |
| kotlin_srcs = ctx.files.kotlin_srcs |
| source_jars = ctx.files.source_jars |
| name = ctx.label.name |
| |
| java_jar = ctx.actions.declare_file(name + ".java.jar") if java_srcs or source_jars else None |
| kotlin_jar = ctx.actions.declare_file(name + ".kotlin.jar") if kotlin_srcs else None |
| |
| deps = [dep[JavaInfo] for dep in ctx.attr.deps + ctx.attr.bundled_deps + ctx.attr.exports] |
| java_info_deps = [dep[JavaInfo] for dep in ctx.attr.deps] |
| |
| # Kotlin |
| jars = [] |
| kotlin_providers = [] |
| if kotlin_srcs: |
| if ctx.attr.stdlib: |
| deps.append(ctx.attr.stdlib[JavaInfo]) |
| java_info_deps.append(ctx.attr.stdlib[JavaInfo]) |
| kotlin_providers += [kotlin_compile( |
| ctx = ctx, |
| name = ctx.attr.module_name, |
| srcs = java_srcs + kotlin_srcs, |
| deps = deps, |
| friends = ctx.files.friends, |
| out = kotlin_jar, |
| jre = ctx.files._bootclasspath, |
| transitive_classpath = True, # Matches Java rules (sans strict-deps enforcement) |
| )] |
| jars += [kotlin_jar] |
| |
| # Resources. |
| if ctx.files.resources or ctx.files.notice: |
| resources_jar = ctx.actions.declare_file(name + ".res.jar") |
| _resources(ctx, ctx.files.resources, ctx.file.notice, resources_jar) |
| jars += [resources_jar] |
| |
| java_toolchain = find_java_toolchain(ctx, ctx.attr._java_toolchain) |
| host_javabase = find_java_runtime_toolchain(ctx, ctx.attr._host_javabase) |
| |
| # Java |
| if java_srcs or source_jars: |
| java_provider = java_common.compile( |
| ctx, |
| source_files = java_srcs, |
| source_jars = source_jars, |
| output = java_jar, |
| deps = deps + kotlin_providers, |
| javac_opts = java_common.default_javac_opts(java_toolchain = java_toolchain) + ctx.attr.javacopts, |
| java_toolchain = java_toolchain, |
| host_javabase = host_javabase, |
| plugins = [plugin[JavaInfo] for plugin in ctx.attr.plugins], |
| ) |
| |
| jars += [java_jar] |
| |
| for dep in ctx.attr.bundled_deps: |
| jars += [java_output.class_jar for java_output in dep[JavaInfo].outputs.jars] |
| |
| run_singlejar( |
| ctx = ctx, |
| jars = jars, |
| out = ctx.outputs.jar, |
| manifest_lines = ctx.attr.manifest_lines, |
| # allow_duplicates = True, # TODO: Ideally we could be more strict here. |
| ) |
| |
| # Create an ijar to improve javac compilation avoidance. |
| ijar = java_common.run_ijar( |
| actions = ctx.actions, |
| jar = ctx.outputs.jar, |
| java_toolchain = java_toolchain, |
| ) |
| |
| bundled_source_jars = [jar for dep in ctx.attr.bundled_deps for jar in dep[JavaInfo].source_jars] |
| _sources(ctx, java_srcs + kotlin_srcs, source_jars + bundled_source_jars, ctx.outputs.source_jar, java_toolchain, host_javabase) |
| |
| java_info = JavaInfo( |
| output_jar = ctx.outputs.jar, |
| compile_jar = ijar, |
| source_jar = ctx.outputs.source_jar, |
| deps = java_info_deps, |
| exports = [dep[JavaInfo] for dep in ctx.attr.exports], |
| runtime_deps = java_info_deps, |
| ) |
| |
| transitive_runfiles = depset(transitive = [ |
| dep[DefaultInfo].default_runfiles.files |
| for dep in ctx.attr.deps + ctx.attr.exports |
| if dep[DefaultInfo].default_runfiles |
| ]) |
| runfiles = ctx.runfiles(files = ctx.files.data, transitive_files = transitive_runfiles) |
| return [ |
| java_info, |
| DefaultInfo(files = depset([ctx.outputs.jar]), runfiles = runfiles), |
| ] |
| |
| _kotlin_library = rule( |
| attrs = { |
| "java_srcs": attr.label_list(allow_files = True), |
| "kotlin_srcs": attr.label_list(allow_files = True), |
| "source_jars": attr.label_list(allow_files = True), |
| "resources": attr.label_list(allow_files = True), |
| "notice": attr.label(allow_single_file = True), |
| "manifest_lines": attr.string_list(), |
| "data": attr.label_list(allow_files = True), |
| "friends": attr.label_list( |
| allow_files = [".jar"], |
| ), |
| "jar": attr.output(mandatory = True), |
| "deps": attr.label_list(providers = [JavaInfo]), |
| "exports": attr.label_list(providers = [JavaInfo]), |
| "bundled_deps": attr.label_list( |
| providers = [JavaInfo], |
| ), |
| "runtime_deps": attr.label_list( |
| providers = [JavaInfo], |
| ), |
| "module_name": attr.string( |
| default = "unnamed", |
| ), |
| "resource_strip_prefix": attr.string(), |
| "javacopts": attr.string_list(), |
| "kotlin_use_ir": attr.bool(), |
| "compress_resources": attr.bool(), |
| "plugins": attr.label_list( |
| providers = [JavaInfo], |
| ), |
| "stdlib": attr.label(), |
| "_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:current_java_toolchain")), |
| "_host_javabase": attr.label(default = Label("@bazel_tools//tools/jdk:current_host_java_runtime")), |
| "_bootclasspath": attr.label( |
| # Use JDK 8 because AGP still needs to support it (b/166472930). |
| default = Label("//prebuilts/studio/jdk:bootclasspath"), |
| allow_files = [".jar"], |
| ), |
| "_kotlinc": attr.label( |
| executable = True, |
| cfg = "host", |
| default = Label("//tools/base/bazel:kotlinc"), |
| allow_files = True, |
| ), |
| "_zipper": attr.label( |
| default = Label("@bazel_tools//tools/zip:zipper"), |
| cfg = "host", |
| executable = True, |
| ), |
| "_singlejar": attr.label( |
| default = Label("@bazel_tools//tools/jdk:singlejar"), |
| cfg = "host", |
| executable = True, |
| ), |
| }, |
| fragments = ["java"], |
| outputs = { |
| "source_jar": "%{name}-src.jar", |
| }, |
| implementation = _kotlin_library_impl, |
| ) |