blob: 71b3ec11307c9522dc2f507012ea227e1b9c9791 [file] [log] [blame]
# 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:defs.bzl", "KtJvmInfo", "TOOLCHAIN_TYPE")
load("//kotlin/internal/jvm:plugins.bzl", "plugins")
load("//kotlin/internal/common:common.bzl", "common")
# MISC UTILS ###########################################################################################################
def _partition_srcs(srcs):
kt_srcs = []
java_srcs = []
src_jars = []
for f in srcs:
if f.path.endswith(".kt"):
kt_srcs.append(f)
elif f.path.endswith(".java"):
java_srcs.append(f)
elif f.path.endswith(".srcjar"):
src_jars.append(f)
kt = depset(kt_srcs)
java = depset(java_srcs)
return struct(
kt = kt,
java = java,
all_srcs = kt + java,
src_jars = depset(src_jars),
)
# JAR ACTIONS ##########################################################################################################
def _fold_jars_action(ctx, rule_kind, output_jar, input_jars):
args = [
"--normalize",
"--compression",
"--deploy_manifest_lines",
"Target-Label: %s" % str(ctx.label),
"Injecting-Rule-Kind: %s" % rule_kind,
"--output",
output_jar.path,
]
for i in input_jars:
args += ["--sources", i.path]
ctx.action(
mnemonic = "KotlinFoldOutput",
inputs = input_jars,
outputs = [output_jar],
executable = ctx.executable._singlejar,
arguments = args,
progress_message = "Merging Kotlin output jar " + output_jar.short_path,
)
_CONVENTIONAL_RESOURCE_PATHS = [
"src/main/resources",
"src/test/resources",
]
def _adjust_resources_path_by_strip_prefix(path, resource_strip_prefix):
if not path.startswith(resource_strip_prefix):
fail("Resource file %s is not under the specified prefix to strip" % path)
clean_path = path[len(resource_strip_prefix):]
return resource_strip_prefix, clean_path
def _adjust_resources_path_by_default_prefixes(path):
for cp in _CONVENTIONAL_RESOURCE_PATHS:
dir_1, dir_2, rel_path = path.partition(cp)
if rel_path:
return dir_1 + dir_2, rel_path
return "", path
def _adjust_resources_path(path, resource_strip_prefix):
if resource_strip_prefix:
return _adjust_resources_path_by_strip_prefix(path, resource_strip_prefix)
else:
return _adjust_resources_path_by_default_prefixes(path)
def _add_resources_cmd(ctx):
res_cmd = []
for f in ctx.files.resources:
c_dir, res_path = _adjust_resources_path(f.short_path, ctx.attr.resource_strip_prefix)
target_path = res_path
if target_path[0] == "/":
target_path = target_path[1:]
line = "{target_path}={c_dir}{res_path}\n".format(
res_path = res_path,
target_path = target_path,
c_dir = c_dir,
)
res_cmd.extend([line])
return "".join(res_cmd)
def _build_resourcejar_action(ctx):
resources = _add_resources_cmd(ctx)
resources_jar_output = ctx.actions.declare_file(ctx.label.name + "-resources.jar")
zipper_arg_path = ctx.actions.declare_file("%s_resources_zipper_args" % ctx.label.name)
ctx.file_action(zipper_arg_path, resources)
cmd = """
rm -f {resources_jar_output}
{zipper} c {resources_jar_output} @{path}
""".format(
path = zipper_arg_path.path,
resources_jar_output = resources_jar_output.path,
zipper = ctx.executable._zipper.path,
)
ctx.action(
mnemonic = "KotlinZipResourceJar",
inputs = ctx.files.resources + [ctx.executable._zipper, zipper_arg_path],
outputs = [resources_jar_output],
command = cmd,
progress_message = "Creating intermediate resource jar %s" % ctx.label,
arguments = [],
)
return resources_jar_output
def kt_jvm_compile_action(ctx, rule_kind, output_jar, srcs):
"""This macro performs a compile operation in a single action.
Args:
rule_kind: The rule kind,
output_jar: The jar file that this macro will use as the output of the action.
module_name: The Kotlin module name, this must be provided and is used by the compiler for symbol mangling in
advanced use cases.
compile_jars: The compile time jars provided on the classpath for the compile operations -- callers are
responsible for preparing the classpath. The stdlib (and jdk7 + jdk8) should generally be added to the classpath
by the caller -- kotlin-reflect could be optional.
friend_paths: A list of jars paths that this compilation unit should have package private access to.
srcs: a struct with the various input sources partitioned.
"""
toolchain = ctx.toolchains[TOOLCHAIN_TYPE]
friends = getattr(ctx.attr, "friends", [])
deps = [d[JavaInfo] for d in friends + ctx.attr.deps] + [toolchain.jvm_stdlibs]
compile_jars = java_common.merge(deps).compile_jars
if len(friends) == 0:
module_name = common.derive_module_name(ctx)
friend_paths = depset()
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:
friend_paths = depset([j.path for j in friends[0][JavaInfo].compile_jars])
module_name = friends[0][KtJvmInfo].module_name
else:
fail("only one friend is possible")
classes_directory = common.declare_output_directory(ctx, "jvm", "classes")
generated_classes_directory = common.declare_output_directory(ctx, "jvm", "generated_classes")
sourcegen_directory = common.declare_output_directory(ctx, "jvm", "sourcegenfiles")
temp_directory = common.declare_output_directory(ctx, "jvm", "temp")
args = common.init_args(ctx, rule_kind, module_name)
args.add("--classdir", classes_directory)
args.add("--sourcegendir", sourcegen_directory)
args.add("--tempdir", temp_directory)
args.add("--kotlin_generated_classdir", generated_classes_directory)
args.add("--output", output_jar)
args.add("--kotlin_output_jdeps", ctx.outputs.jdeps)
args.add("--kotlin_output_srcjar", ctx.outputs.srcjar)
args.add("--kotlin_friend_paths", "\n".join(friend_paths.to_list()))
args.add("--classpath", compile_jars)
args.add_all("--sources", srcs.all_srcs, omit_if_empty = True)
args.add_all("--source_jars", srcs.src_jars, omit_if_empty = True)
# Collect and prepare plugin descriptor for the worker.
plugin_info = plugins.merge_plugin_infos(ctx.attr.plugins + ctx.attr.deps)
if len(plugin_info.annotation_processors) > 0:
args.add("--kotlin_plugins", plugin_info.to_json())
progress_message = "Compiling Kotlin to JVM %s { kt: %d, java: %d, srcjars: %d }" % (
ctx.label,
len(srcs.kt),
len(srcs.java),
len(srcs.src_jars),
)
inputs, _, input_manifests = ctx.resolve_command(tools = [toolchain.kotlinbuilder, toolchain.kotlin_home])
ctx.actions.run(
mnemonic = "KotlinCompile",
inputs = depset(inputs) + ctx.files.srcs + compile_jars,
outputs = [
output_jar,
ctx.outputs.jdeps,
ctx.outputs.srcjar,
sourcegen_directory,
classes_directory,
temp_directory,
generated_classes_directory,
],
executable = toolchain.kotlinbuilder.files_to_run.executable,
execution_requirements = {"supports-workers": "1"},
arguments = [args],
progress_message = progress_message,
input_manifests = input_manifests,
)
# create the java provider and the kotlin provider. Whilst a struct is being returned, and this is a valid way of
# creating a provider, it is intended that the client transforms this into an form.
return struct(
java = JavaInfo(
output_jar = ctx.outputs.jar,
compile_jar = ctx.outputs.jar,
source_jar = ctx.outputs.srcjar,
# jdeps = ctx.outputs.jdeps,
deps = deps,
runtime_deps = [d[JavaInfo] for d in ctx.attr.runtime_deps],
exports = [d[JavaInfo] for d in getattr(ctx.attr, "exports", [])],
neverlink = getattr(ctx.attr, "neverlink", False),
),
kt = KtJvmInfo(
srcs = ctx.files.srcs,
module_name = module_name,
# intelij aspect needs this.
outputs = struct(
jdeps = ctx.outputs.jdeps,
jars = [struct(
class_jar = ctx.outputs.jar,
ijar = None,
source_jars = [ctx.outputs.srcjar],
)],
),
),
)
def kt_jvm_produce_jar_actions(ctx, rule_kind, src_jars = []):
"""Setup a kotlin compile action. This method takes care of all of the aspects of producing a jar.
Specifically this action will conditionally set up actions to fold resources and resourcejars and merge them onto a
jar compiled by the builder. It indirects the output_jar -- i.e., if no resources or resource jars are present it
won't do anything.
Args:
ctx: The rule context.
Returns:
A JavaInfo struct for the output jar that this macro will build.
"""
# The main output jars
output_jar = ctx.outputs.jar
# The output of the compile step may be combined (folded) with other entities -- e.g., other class files from annotation processing, embedded resources.
kt_compile_output_jar = output_jar
# the list of jars to merge into the final output, start with the resource jars if any were provided.
output_merge_list = ctx.files.resource_jars
# If this rule has any resources declared setup a zipper action to turn them into a jar and then add the declared zipper output to the merge list.
if len(ctx.files.resources) > 0:
output_merge_list = output_merge_list + [_build_resourcejar_action(ctx)]
# If this compile operation requires merging other jars setup the compile operation to go to a intermediate file and add that file to the merge list.
if len(output_merge_list) > 0:
# Intermediate jar containing the Kotlin compile output.
kt_compile_output_jar = ctx.new_file(ctx.label.name + "-ktclass.jar")
# If we setup indirection than the first entry in the merge list is the result of the kotlin compile action.
output_merge_list = [kt_compile_output_jar] + output_merge_list
srcs = _partition_srcs(ctx.files.srcs)
if (len(srcs.kt) + len(srcs.java) == 0) and len(srcs.src_jars) == 0:
fail("no sources provided")
# setup the merge action if needed.
if len(output_merge_list) > 0:
_fold_jars_action(ctx, rule_kind, output_jar, output_merge_list)
# setup the compile action.
return kt_jvm_compile_action(
ctx,
rule_kind = rule_kind,
output_jar = kt_compile_output_jar,
srcs = srcs,
)