blob: bd8a7dc2967cae7fe960539bab5beeb73a5112f0 [file] [log] [blame]
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,
)