blob: d3b5858031dbb50c058752ca57f4a43146aa002b [file] [log] [blame]
# Copyright 2014 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.
"""Java rules for bootstraping Bazel.
This is a quick and dirty rule to make Bazel compile itself. It's not production
ready.
"""
load("@rules_java//java/common:java_common.bzl", "java_common")
_JarsInfo = provider(fields = ["compile_time_jars", "runtime_jars"])
def _join_paths(separator, files):
"""Joins the paths of the files separated by the given separator.
Args:
separator: string to separate the files paths
files: list of files to join their paths
"""
return separator.join([f.path for f in files])
def _java_library_impl(ctx):
javac_options = ctx.fragments.java.default_javac_flags
class_jar = ctx.outputs.class_jar
compile_time_jars = depset(order = "topological")
runtime_jars = depset(order = "topological")
for dep in ctx.attr.deps:
compile_time_jars = depset(
transitive = [compile_time_jars, dep[_JarsInfo].compile_time_jars],
)
runtime_jars = depset(
transitive = [runtime_jars, dep[_JarsInfo].runtime_jars],
)
jars = ctx.files.jars
neverlink_jars = ctx.files.neverlink_jars
compile_time_jars = depset(jars + neverlink_jars, transitive = [compile_time_jars])
runtime_jars = depset(jars, transitive = [runtime_jars])
compile_time_jars_list = compile_time_jars.to_list() # TODO: This is weird.
build_output = class_jar.path + ".build_output"
java_output = class_jar.path + ".build_java"
javalist_output = class_jar.path + ".build_java_list"
sources = ctx.files.srcs
sources_param_file = ctx.actions.declare_file(class_jar.basename + "-2.params")
ctx.actions.write(
output = sources_param_file,
content = _join_paths("\n", sources),
is_executable = False,
)
# Cleaning build output directory
cmd = "set -e;rm -rf " + build_output + " " + java_output + " " + javalist_output + "\n"
cmd += "mkdir " + build_output + " " + java_output + "\n"
files = " @" + sources_param_file.path
java_runtime = ctx.attr._jdk[java_common.JavaRuntimeInfo]
jar_path = "%s/bin/jar" % java_runtime.java_home
if ctx.files.srcjars:
files += " @" + javalist_output
for file in ctx.files.srcjars:
cmd += "%s tf %s | grep '\\.java$' | sed 's|^|%s/|' >> %s\n" % (jar_path, file.path, java_output, javalist_output)
cmd += "unzip %s -d %s >/dev/null\n" % (file.path, java_output)
if ctx.files.srcs or ctx.files.srcjars:
cmd += "%s/bin/javac" % java_runtime.java_home
cmd += " " + " ".join(javac_options)
if compile_time_jars:
cmd += " -classpath '" + _join_paths(ctx.configuration.host_path_separator, compile_time_jars_list) + "'"
cmd += " -d " + build_output + files + "\n"
# We haven't got a good story for where these should end up, so
# stick them in the root of the jar.
for r in ctx.files.resources:
cmd += "cp %s %s\n" % (r.path, build_output)
cmd += (jar_path + " cf " + class_jar.path + " -C " + build_output + " .\n" +
"touch " + build_output + "\n")
ctx.actions.run_shell(
inputs = (sources + compile_time_jars_list + [sources_param_file] +
ctx.files._jdk + ctx.files.resources + ctx.files.srcjars),
outputs = [class_jar],
mnemonic = "JavacBootstrap",
command = cmd,
use_default_shell_env = True,
)
runfiles = ctx.runfiles(collect_data = True)
compile_time_jars = depset(transitive = [compile_time_jars], direct = [class_jar])
runtime_jars = depset(transitive = [runtime_jars], direct = [class_jar])
return [
DefaultInfo(
files = depset([class_jar]),
runfiles = runfiles,
),
_JarsInfo(
compile_time_jars = compile_time_jars,
runtime_jars = runtime_jars,
),
]
def _java_binary_impl(ctx):
library_result = _java_library_impl(ctx)
deploy_jar = ctx.outputs.deploy_jar
manifest = ctx.outputs.manifest
build_output = deploy_jar.path + ".build_output"
main_class = ctx.attr.main_class
java_runtime = ctx.attr._jdk[java_common.JavaRuntimeInfo]
jar_path = "%s/bin/jar" % java_runtime.java_home
ctx.actions.write(
output = manifest,
content = "Main-Class: " + main_class + "\n",
is_executable = False,
)
# Cleaning build output directory
cmd = "set -e;rm -rf " + build_output + ";mkdir " + build_output + "\n"
for jar in library_result[1].runtime_jars.to_list():
cmd += "unzip -qn " + jar.path + " -d " + build_output + "\n"
cmd += (jar_path + " cmf " + manifest.path + " " +
deploy_jar.path + " -C " + build_output + " .\n" +
"touch " + build_output + "\n")
ctx.actions.run_shell(
inputs = library_result[1].runtime_jars.to_list() + [manifest] + ctx.files._jdk,
outputs = [deploy_jar],
mnemonic = "Deployjar",
command = cmd,
use_default_shell_env = True,
)
# Write the wrapper.
executable = ctx.outputs.executable
ctx.actions.write(
output = executable,
content = "\n".join([
"#!/bin/bash",
"# autogenerated - do not edit.",
"case \"$0\" in",
"/*) self=\"$0\" ;;",
"*) self=\"$PWD/$0\";;",
"esac",
"",
"if [[ -z \"$JAVA_RUNFILES\" ]]; then",
" if [[ -e \"${self}.runfiles\" ]]; then",
" export JAVA_RUNFILES=\"${self}.runfiles\"",
" fi",
" if [[ -n \"$JAVA_RUNFILES\" ]]; then",
" export TEST_SRCDIR=${TEST_SRCDIR:-$JAVA_RUNFILES}",
" fi",
"fi",
"",
"jvm_bin=%s" % (ctx.attr._jdk[java_common.JavaRuntimeInfo].java_executable_exec_path),
"if [[ ! -x ${jvm_bin} ]]; then",
" jvm_bin=$(which java)",
"fi",
# We extract the .so into a temp dir. If only we could mmap
# directly from the zip file.
"DEPLOY=$(dirname $self)/$(basename %s)" % deploy_jar.path,
# This works both on Darwin and Linux, with the darwin path
# looking like tmp.XXXXXXXX.{random}
"SO_DIR=$(mktemp -d -t tmp.XXXXXXXXX)",
"function cleanup() {",
" rm -rf ${SO_DIR}",
"}",
"trap cleanup EXIT",
"unzip -q -d ${SO_DIR} ${DEPLOY} \"*.so\" \"*.dll\" \"*.dylib\" >& /dev/null",
("${jvm_bin} -Djava.library.path=${SO_DIR} %s -jar $DEPLOY \"$@\"" %
" ".join(ctx.attr.jvm_flags)),
"",
]),
is_executable = True,
)
runfiles = ctx.runfiles(files = [deploy_jar, executable] + ctx.files._jdk, collect_data = True)
files_to_build = depset(
transitive = [library_result[0].files],
direct = [deploy_jar, manifest, executable],
)
return [DefaultInfo(files = files_to_build, runfiles = runfiles)]
def _java_import_impl(ctx):
# TODO(bazel-team): Why do we need to filter here? The attribute
# already says only jars are allowed.
jars = depset(ctx.files.jars)
neverlink_jars = depset(ctx.files.neverlink_jars)
runfiles = ctx.runfiles(collect_data = True)
compile_time_jars = depset(transitive = [jars, neverlink_jars])
return [
DefaultInfo(files = jars, runfiles = runfiles),
_JarsInfo(
compile_time_jars = compile_time_jars,
runtime_jars = jars,
),
]
java_library_attrs = {
"_jdk": attr.label(
default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
providers = [java_common.JavaRuntimeInfo],
cfg = "exec",
),
"data": attr.label_list(allow_files = True),
"resources": attr.label_list(allow_files = True),
"srcs": attr.label_list(allow_files = [".java"]),
"jars": attr.label_list(allow_files = [".jar"]),
"neverlink_jars": attr.label_list(allow_files = [".jar"]),
"srcjars": attr.label_list(allow_files = [".jar", ".srcjar"]),
"deps": attr.label_list(
allow_files = False,
providers = [_JarsInfo],
),
}
java_library = rule(
_java_library_impl,
attrs = java_library_attrs,
outputs = {
"class_jar": "lib%{name}.jar",
},
fragments = ["java", "cpp"],
)
# A copy to avoid conflict with native rule.
bootstrap_java_library = rule(
_java_library_impl,
attrs = java_library_attrs,
outputs = {
"class_jar": "lib%{name}.jar",
},
fragments = ["java"],
)
java_binary_attrs_common = dict(java_library_attrs)
java_binary_attrs_common.update({
"jvm_flags": attr.string_list(),
"jvm": attr.label(default = Label("@bazel_tools//tools/jdk:jdk"), allow_files = True),
})
java_binary_attrs = dict(java_binary_attrs_common)
java_binary_attrs["main_class"] = attr.string(mandatory = True)
java_binary_outputs = {
"class_jar": "lib%{name}.jar",
"deploy_jar": "%{name}_deploy.jar",
"manifest": "%{name}_MANIFEST.MF",
}
java_binary = rule(
_java_binary_impl,
executable = True,
attrs = java_binary_attrs,
outputs = java_binary_outputs,
fragments = ["java", "cpp"],
)
# A copy to avoid conflict with native rule
bootstrap_java_binary = rule(
_java_binary_impl,
executable = True,
attrs = java_binary_attrs,
outputs = java_binary_outputs,
fragments = ["java"],
)
java_test = rule(
_java_binary_impl,
executable = True,
attrs = dict(list(java_binary_attrs_common.items()) + [
("main_class", attr.string(default = "org.junit.runner.JUnitCore")),
# TODO(bazel-team): it would be better if we could offer a
# test_class attribute, but the "args" attribute is hard
# coded in the bazel infrastructure.
]),
outputs = java_binary_outputs,
test = True,
fragments = ["java", "cpp"],
)
java_import = rule(
_java_import_impl,
attrs = {
"jars": attr.label_list(allow_files = [".jar"]),
"srcjar": attr.label(allow_files = [".jar", ".srcjar"]),
"neverlink_jars": attr.label_list(allow_files = [".jar"], default = []),
},
)